代理模式:静态代理、JDK动态代理、Cglib动态代理

一、代理模式

1. 概念:为一个对象提供一种代理,用代理控制对象的访问,而不是直接访问对象

2. 使用场景:

a. 远程代理:位于两个不同的地址空间对象的访问

b. 虚拟代理:消耗资源较少的对象代表消耗资源多的对象

c. 缓冲代理:缓存

d. 保护代理:控制对一个对象的不同级别的访问权限

e. 智能引用:为一个对象的访问(引用)提供一些额外的操作,比如校验、记日志

二、静态代理

1. 编译时确定了代理与被代理者的关系,又叫编译时增强

2. 静态代理由接口、业务实现类、业务代理类组成

a. 业务实现类实现业务接口,负责具体的业务

b. 业务代理类也要实现业务接口,负责拦截、过滤、预处理

3. 使用时,不是通过调用业务实现类的方法,而是通过调用业务代理类的同名方法,先创建业务实现类的对象,传入业务代理类的构造方法里

4. 缺点:一个业务代理类只能对一个业务实现类进行包装,不能对多个;多个业务代码会有重复

三、动态代理:

1. 运行时才真正确定代理关系,又叫运行时增强

2. 有一个代理类,能代理所有业务实现类的方法调用

3. 根据传进来的业务实现类和方法名进行具体调用

4. 与静态代理比较:可以对多个对象进行代理,比静态代理更灵活,不用写多余代码;而静态代理对多个对象进行代理需要写多个代理类

四、JDK动态代理,针对接口,核心是InvocationHandler

1. 在程序调用到代理类对象时,才由JVM真正创建

2. JVM根据业务实现类对象和方法名,动态创建一个代理类.class文件被字节码执行,在调用方法时通过这个代理类对象里的方法调用

3. 业务类实现业务接口,如果没有业务接口,不能使用JDK动态代理

4. 代理类通过实现InvocationHandler接口创建动态代理类,而不需要实现业务接口,所有代理类都一个写法是通用的

public class BookFacadeProxy implements InvocationHandler {  // InvocationHandler接口,被动态代理类实现,负责处理被代理对象的操作
    private Object target;//这其实业务实现类对象,用来调用具体的业务方法 
    /** 
     * 绑定业务对象并返回一个代理类  
     */  
    public Object bind(Object target) {  
        this.target = target;  //接收业务实现类对象参数

       // 通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
       // 创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                target.getClass().getInterfaces(), this); }  
    /** 
     * 包装调用方法:进行预处理、调用后处理 
     */  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        Object result=null;  

        System.out.println("预处理操作——————");  
        //调用真正的业务方法  
        result=method.invoke(target, args);  

        System.out.println("调用后处理——————");  
        return result;  
    }  
  
}

5. 经过上面两步,已经创建了业务实现类对象和代理类对象,将其绑定,绑定的时候就是通过反射,然后代理使用

public static void main(String[] args) {  
        BookFacadeImpl bookFacadeImpl=new BookFacadeImpl();
        BookFacadeProxy proxy = new BookFacadeProxy();  // 创建动态代理类实例
        BookFacade bookfacade = (BookFacade) proxy.bind(bookFacadeImpl);  // 绑定才能动态代理
        bookfacade.addBook();  
    }

5. JDK动态代理的缺点:只能代理实现了接口的业务类方法,如果业务类自己定义的方法,则不能代理

五、Cglib(Code Generation Library)动态代理,针对类和接口,核心是MethodInterceptor

1. Cglib是代码生成类库,可以在运行期间动态扩展类和接口,底层使用Java字节码操作框架ASM实现

2. 原理:对要代理的业务类,生成一个子类,并覆盖业务类里的所有非final方法,在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势织入横切逻辑

3. 缺点:对于final方法,无法进行代理

4. 使用:

a. 定义业务类,不需要实现任何接口

b. 定义拦截器,在调用目标方法时,Cglib会回调MethodInterceptor接口里的intercept方法进行拦截

public class BookFacadeCglib implements MethodInterceptor {  
    private Object target;//业务类对象,供代理方法中进行真正的业务方法调用
  
    //相当于JDK动态代理中的绑定
    public Object getInstance(Object target) {  
        this.target = target;  //给业务对象赋值
        Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类,核心实现
        enhancer.setSuperclass(this.target.getClass());  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this); 
       // 创建动态代理类对象并返回  
       return enhancer.create(); 
    }
    // 实现回调方法 
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
        System.out.println("预处理——————");
        proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
        System.out.println("调用后操作——————");
        return null; 
    }

六、总结

1. 静态代理比动态代理更符合OOP原则,在日常开发中使用也比较多 

2. 动态代理在开发框架的时候用的比较多

3. Cglib动态代理运行方法时的性能,比使用反射的JDK动态代理要快,但是在创建对象的时候比较慢,所以适用场景不一样,无需频繁创建代理对象时(比如单例的代理对象),使用cglib,其他场景使用JDK

4. Spring AOP默认使用JDK,当没有接口时,会强制使用cglib

参考:

https://blog.csdn.net/ShuSheng0007/article/details/80864854

原文地址:https://www.cnblogs.com/june0816/p/6478100.html