什么是代理
代理是一种软件设计模式,通过不直接访问被代理对象的方式,而访问代理对象的方法。代理既可以做原对象的增强也可以做原对象的替代。
代理适用的场景
设计模式中有一个设计原则是开闭原则,在不修改原来代码的基础上,我们可以采用代理的方式对类进行功能增强
在RPC框架中,通过代理接口来模拟访问远程服务的方法
Spring的AOP机制就是采用动态代理的机制来实现切面编程
静态代理与动态代理的区别
我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制
动态代理:InvocationHandler角色的由来
有上图可以看出,代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。那么,为了构造出具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler
。
动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法
在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。
还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能
在Java中提供了两种常见的创建动态代理的机制,就是按照以上两种设计思路,JDK动态代理(实现接口)和CGlib动态代理(继承类)
JDK动态代理(通过接口)
解析
比如现在想为RealSubject这个类创建一个动态代理对象,JDK主要会做以下工作:
1.获取 RealSubject上的所有接口列表;
2.确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;
3.根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;
4.将对应的字节码转换为对应的class 对象;
5.创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
6.Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象;
JDK通过java.lang.reflect.Proxy
包来支持动态代理,一般情况下,我们使用下面的newProxyInstance()
1 | //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序 |
而对于InvocationHandler,我们需要实现它的invoke()
,在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法
1 | //在代理实例上处理方法调用并返回结果 |
通过观察生成的动态代理类具有以下几个特点:
1.继承自 java.lang.reflect.Proxy,实现了 Rechargable,Vehicle 这两个ElectricCar实现的接口;
2.类中的所有方法都是final的;
3.所有的方法功能的实现都统一调用了InvocationHandler的invoke()方法;
demo
1 | //继承InvocationHandler实现invoke |
CGlib动态代理(通过类继承)
解析
JDK中提供的生成动态代理类的机制有个鲜明的特点是:某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法。如果某个类没有实现接口,那么这个类就不能同JDK产生动态代理了,而可以使用CGLIB(Code Generation Library)
,通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。和JDK动态代理一样,底层是处理字节码,cglib创建某个类A的动态代理类的模式是:
1.查找A上的所有非final的public类型的方法定义;
2.将这些方法的定义转换成字节码;
3.将组成的字节码转换成相应的代理的class对象;
4.实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
demo
1 | public class CGLibProxy implements MethodInterceptor { |
原理区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
Spring在选择用JDK还是CGLib的依据
当Bean实现接口时,Spring就会用JDK的动态代理
当Bean没有实现接口时,Spring使用CGLib来实现