动态代理的意义在于生成一个占位,代理真实对象,从而控制正式对象的访问。代理的作用是在真实对象访问之前或者之后加入对应的逻辑,或者根据其他的规则控制是否使用真实对象。代理需分为两步:代理对象和真实对象建立代理关系;实现代理对象的代理逻辑方法。在java中有多种动态代理机制,如JDK、CGLIB、Javassist等。
1. JDK动态代理
该方法需要借助一个接口才能产生代理对象,首先定义接口:
随后提供实现类来实现接口:
在JDK动态代理中,实现代理逻辑类必须实现java.lang.reflect.InvocationHandler接口,其里面定义一个invoke方法,并提供接口数组用于下挂代理对象。
第一步:建立代理对象和真实对象的关系
使用bind方法完成,方法里面先用类的属性target保存真实对象,通过如下代码建立生成代理对象。
newProxyInstance方法包含3个参数:
第一个是类加载器,使用target本身的类加载器;第二个将生成的动态代理对象下挂在那些接口下;第三个是定义实现方法逻辑的代理类,this表示当前对象,必须实现InvocationHandler接口的invoke方法,是代理逻辑方法的现实方法。
第二步:实现代理逻辑方法
invoke的三个参数含义如下所示。
proxy:代理对象,是bind方法生成的对象;method:当前调度的方法;args:调度方法的参数。
测试JDK动态代理:
首先通过bind方法绑定代理关系,然后在代理对象调度方法时进入代理的逻辑,测试结果如下:
2. CGLIB动态代理
由于JDK动态代理需要提供接口,而CGLIB动态代理不需要接口,只需要一个非抽象类就可实现动态代理。
用到CGLIB的Enhancer,通过设置超类的方法,setCallback方法设置哪个类为代理类。参数this意味着是当前对象,用this这个对象实现接口的方法,返回代理对象。测试一下CGLIB动态代理:
3. 拦截器
使用JDK动态代理实现一个拦截器的逻辑,先定义拦截器接口Interceptor:
定义三个方法,三个方法的参数:proxy代理对象,target真实对象,method方法,args为运行方法参数。
before方法返回boolean值,在真实对象前调用。当返回为true时,则返回真实对象的方法;当返回为false则调用around方法。随后调用after方法。
实现接口Interceptor的实现类:
在JDK动态代理中使用拦截器的代码如下:
以上代码执行过程:
1.在bind方法中用JDK动态代理绑定一个对象,然后返回代理对象。
2.如果没有设置拦截器则直接反射真实对象的方法,然后结束;否则进行第三步;
3.通过反射生成拦截器并准备使用它。
4.调用拦截器的before方法,当返回为true则反射原来的方法否则运行拦截器的around方法。
5.调用拦截器的after方法
6.返回结果。
拦截器的工作流程:
拦截器进一步简化代理是使用方法:
4. 责任链模式
当一个对象在一条链上被多个拦截器拦截处理,我们把这样的设计模式称为责任链模式,用于一个对象在多个角色中传递。
例如:
一个程序员需要请假一周。如果把请假申请单看成 个对象,那么它需要经过项目经理、部门经理、人事等多个角色的审批,每个角色都有机会通过拦截这个申请单进行审批或者修改。这时就要考虑提供项目经理、部门经理和人事的处理逻辑,所以需要提供三个拦截器,而传递的则是请假申请单。
可以考虑用层层代理来实现,就是当申请单(target )走到项目经理处,使用第 个动态代理 proxyl 。当它走到部门经理处,部门经理会得到 个在项目经理的代理 proxyl 基础上生成的 proxy2 来处理经理的逻辑。当它走到人事处,会在 proxy2 基础生成 proxy3。
责任链拦截器接口的定义:
测试责任链上的多拦截器:
测试结果如下:
责任链模式的优点在于我们可以在传递链上加入新的拦截器,增加拦截逻辑,其缺点是会增加代理和反射,而代理和反射的性能不高。