Spring—AOP(静态代理与动态代理)+案例分析

介绍AOP

AOP为(Aspect Oriented Programming)的缩写,意为:
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 实现的关键在于 代理模式,AOP 代理主要分为静态代理和动态代理。

静态代理

所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,
他会在编译阶段将 AspectJ(切面)织入到 Java 字节码中,运行的时候就是增强之后的 AOP 对象。

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现

  • 真实角色 : 被代理的角色

  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般用于附属的操作 .

  • 客户 : 对代理角色进行操作.

案例一

租房案例分析:

  1. 实现一个租房的接口,抽象类角色
//租房
public interface Rent {
    public void rent();
}
  1. 创建一个真实代理角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}
  1. 代理角色
//代理角色:中介
public class Proxy implements Rent {
   //获取房东
   private Host host;
   //生成有参与无参构造
   public Proxy() {
    }
   public Proxy(Host host) {
       this.host = host;
  }

   //代理帮房东租房
   public void rent(){
   	   //看房
       seeHouse();
       //房东要出粗房子
       host.rent();
       //签合同
       contract();
       //收中介费
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("中介带客户看房");
  }
   //签租赁合同
   public void contract(){
       System.out.println("签租赁合同");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}
  1. 客户访问代理角色
//客户去找中介代理
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //代理:中介帮房东租房,代理角色会有一些附属操作
       Proxy proxy = new Proxy(host);

       //客户去找中介
       proxy.rent();
  }
}

案例二

实现增删改查的业务实现:

  1. 创建抽象角色
//实现增删改查
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}
  1. 创建真实角色,实现增删改查操作
//实现增删改查操作
public class UserServiceImpl implements UserService {

   public void add() {
       System.out.println("增加了一个用户");
  }

   public void delete() {
       System.out.println("删除了一个用户");
  }

   public void update() {
       System.out.println("更新了一个用户");
  }

   public void query() {
       System.out.println("查询了一个用户");
  }
}
  1. 创建代理角色,完成一些附属功能比如:添加一个日志
//通过代理角色,增加附属操作:日志
public class UserServiceProxy implements UserService {
   //调用真实角色
   private UserServiceImpl userService;
   //重写set方法
   public void setUserService(UserServiceImpl userService) {
       this.userService = userService;
  }

   public void add() {
       log("add");
       userService.add();
  }

   public void delete() {
       log("delete");
       userService.delete();
  }

   public void update() {
       log("update");
       userService.update();
  }

   public void query() {
       log("query");
       userService.query();
  }
   //日志方法
   public void log(String debug){
       System.out.println("使用了"+debug+"方法");
  }
}
  1. 测试
public class Client {
   public static void main(String[] args) {
       //真实角色
       UserServiceImpl userService = new UserServiceImpl();
       //代理角色
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理角色实现功能
       proxy.setUserService(userService);

       proxy.add();
  }
}

代理模式的好处

  • 代理模式能将代理对象与真实被调用的目标对象分离

  • 一定程度上降低了系统的耦合度

  • 公共业务发生扩展的时候方便集中管理

代理模式的缺点

  • 代理模式会造成系统设计中类的数量增加

  • 一个真实角色就会生产一个代理角色,代码量也会翻倍,开发效率也会变低

动态代理

动态代理首先需要了解InvocationHandler接口 和 Proxy类

InvocationHandler接口 和 Proxy类

InvocationHandler接口:

  1. 是由代理实例的调用处理程序实现的接口

  2. 每个代理实例都有一个关联的调用处理程序,当再代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法

Proxy类:

  1. 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
  2. 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler

所谓的动态代理就是说 AOP 框架不会去修改字节码,
而是每次运行时在内存中临时为方法生成一个 AOP 对象,
这个AOP 对象包含了目标对象的全部方法,
并且在特定的切点做了增强处理,并回调原对象的方法。

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口: JDK动态代理
    • 基于类: CGLIB动态代理
    • java字节码实现:javasist

案例一

还是通过租房案例来进行分析

  1. 创建一个抽象角色与真实角色
//抽象角色:租房
public interface Rent {
    public void rent();
}
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}
  1. 创建一个代理角色,此时的代理角色会自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    //第一个参数,获取ClassLoader,加载类到那个位置
    //第二个参数,获取要代理的抽象角色,也就是代理的接口是谁
    //第三个参数,获取InvocationHandler来处理
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }

    // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
    // 处理代理实例上的方法调用并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //method是一个反射对象
        //通过method调用invoke方法
        //将rent接口方法传入
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    //看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }

}
  1. 测试
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色
        //通过调用程序处理角色来处理要调用的接口对象
        //因为真实角色与代理角色都会共同实现一个接口
        //真实角色已经实现了接口
        //代理角色要通过:代理角色的处理程序(ProxyInvocationHandler)来实现
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //将真实角色放置进去,就相当于实现了接口
        handler.setRent(host);
        //使用处理程序中的getProxy动态生成对应的代理类
        //这里的proxy是动态生成的
        Rent proxy = (Rent)handler.getProxy();
        proxy.rent();
    }
}

案例二

利用增删查改这个案例

  1. 编写抽象角色与真实角色
//抽象角色
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();

}
//真实对象
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("添加用户");
    }

    public void delete() {
        System.out.println("删除用户");
    }

    public void update() {
        System.out.println("修改用户");
    }

    public void query() {
        System.out.println("查询用户");
    }
}
  1. 编写一个通用的动态代理实现的类,将所有的代理对象设置为Object
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    // 处理代理实例上的方法调用并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过method来动态获取名字
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    //日志方法
    public void log(String debug){
        System.out.println("使用了"+debug+"方法");
    }

}
  1. 测试
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //设置要代理的对象
        handler.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService) handler.getProxy();
        proxy.add();
    }
}

动态代理的好处

  • 代理模式能将代理对象与真实被调用的目标对象分离

  • 一定程度上降低了系统的耦合度

  • 公共业务发生扩展的时候方便集中管理

  • 一个动态代理类代理一个接口 , 一般代就是对应的一类业务

  • 一个动态代理可以代理多个类,只要实现的是同一个接口即可


版权声明:本文为Falling_stars_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。