代理模式其实有两大类:静态代理与动态代理,之前的例子中,采用实现同一接口的方式来显式创建的代理类的方式属于静态代理模式,这种模式简单易理解,缺点就是一旦需要进行代理模式设计的类有很多,需要为每个类创建一个代理类,即为繁琐,这样,动态代理应运而生,使用动态代理,可以为多个类创建代理。
动态代理有两种方式,一种是JDK实现的动态代理,一种是CGLIB实现的动态代理,其中JDK动态代理是基于接口的,CGLIB是基于类的,明显CGLIB要高效一些,因为它免去了接口的消耗与限制。
1、JDK动态代理
JDK动态代理是基于接口实现的,采用的是反射原理。
要使用JDK动态代理需要实现InvocationHandler接口,InvocationHandler接口是JDK专为动态代理而设计的接口,其有数个实现类,是JDK中使用动态代理来实现一些其他的功能,我们的实现类似于它们,完全可以参考其代码。
InvocationHandler接口中只有一个方法 invoke,这个方法用于调用具体的实现来完成功能,调用的过程需要借助于Java反射原理。
还是之前的实例:
自然人接口:ZiRanRen
1 public interface ZiRanRen {2 void quanli();3 }
马云:MaYun
1 public class MaYun implements ZiRanRen { 2 public void eat() { 3 System.out.println("今天吃满汉全席"); 4 } 5 public void drink() { 6 System.out.println("今天喝大西洋"); 7 } 8 @Override 9 public void quanli() {10 System.out.println("我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务");11 }12 }
InvocationHandler实例:MyInvocationHandler
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 4 public class MyInvocationHandler implements InvocationHandler{ 5 private Object target; 6 MyInvocationHandler(){super();} 7 MyInvocationHandler(Object target){ 8 super(); 9 this.target = target;10 }11 @Override12 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {13 if("quanli".equals(method.getName())){14 return method.invoke(target, args);15 }else 16 return method.invoke(target, args);17 }18 }
测试类:Clienter
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Proxy; 3 4 public class Clienter { 5 public static void main(String[] args) { 6 //动态代理实现 7 ZiRanRen ls = new MaYun(); 8 InvocationHandler invocationHandler = new MyInvocationHandler(ls); 9 ZiRanRen Proxyer = (ZiRanRen)Proxy.newProxyInstance(ls.getClass().getClassLoader(), ls.getClass().getInterfaces(), invocationHandler);10 Proxyer.quanli();11 }12 }
执行结果:
我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务
代码解析:
(1)MyInvocationHandler实现类中定义的Object target中的target指的是接口的实现类,即之前所说的委托类,本段代码中指的就是MaYun类,这个从最后测试类中第7、8行可以看出,我们将MyYun类的实例作为参数来生成MyInvocationHandler实例。
(2)代码编写的阶段并未显式调用MaYun类的quanli方法,但是最后却是实实在在被调用了,这时怎么回事呢?
其实这正是动态代理的魅力所在,动态代理的动态二字的含义是不局限于某一个具体类,当我们实现了一个InvocationHandler接口的实例之后,就可以使用这个实例进行任意类的代理,被代理的类是在运行时才会确定(这也正是反射的能力之所在),编码阶段根本无从知晓,复用性极强。
(3)InvocationHandler接口的三个参数问题:第一个参数proxy表示的是被代理的实例(此处指MaYun类的实例ls),第二个参数method代表要执行的被代理类中的方法(此处指MaYun类中的quanli方法),第三个参数表示被代理类中方法的参数列表(此处指MaYun类的quanli方法的参数列表)。如此看来,InvocationHandler的三个参数就是精确指定要执行哪个类的哪个方法,参数是***。当然在代码中还是泛指,这个精确要到程序运行时才能真正体现。
(4)测试类中创建代理类实例的模式与静态代理的模式一致,表示与被代理类实现同一接口。使用Proxy代理类的静态方法newProxyInstance方法来完成代理类实例的创建,其有三个参数:第一个是实例类的加载器,第二个是实例类实现的接口,第三个是InvocationHandler接口的实例。
2、CGLIB动态代理
CGLIB动态代理不同于JDK提供的动态代理,它不再基于接口,而是直接基于被代理类来完成代理模式。我们可以将CGLIB看作是一个代理类的生成工具,通过编程的方式来完成一个类的创建及实例的创建,不同于我们以前直接使用Class来创建类的方式,明显复杂性提升很多,但是CGLIB将这种复杂的类生成功能屏蔽化,我们只需要借助于它所提供的API就可以完成代理类的生成和实例的创建。
要使用CGLIB,需要向导入cglib的jar包,此处我们使用:cglib-nodep-2.2.2.jar。
参见如下示例代码:
被代理类:MaYun
1 public class MaYun { 2 public void eat() { 3 System.out.println("今天吃满汉全席"); 4 } 5 public void drink() { 6 System.out.println("今天喝大西洋"); 7 } 8 public void quanli() { 9 System.out.println("我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务");10 }11 }
方法回调器:Cgliber
1 import java.lang.reflect.Method; 2 3 import net.sf.cglib.proxy.Enhancer; 4 import net.sf.cglib.proxy.MethodInterceptor; 5 import net.sf.cglib.proxy.MethodProxy; 6 /** 7 * Cglib就犹如一个代码式的类生成器,一般手动创建类的内容涉及到的东西,都需要通过Cglib提供的API来完成。 8 * 如下代码中:设置超类和设置回调,对应于正常类编写时,编写的继承类与方法回调 9 * 这里实现的是MethodInterceptor接口(方法拦截器),这个接口的作用就是实现方法拦截(回调)10 *11 */12 public class Cgliber implements MethodInterceptor{13 private Object target;//这个target指的就是被代理类的实例14 /*15 * 生成代理实例(依据被代理类来生成代理类),Enhancer则是代理类生成工具16 */17 public Object getInstance(Object target){18 this.target = target;19 Enhancer enhancer = new Enhancer();20 enhancer.setSuperclass(this.target.getClass());//设置被继承类(超类)21 enhancer.setCallback(this);//设置回调22 return enhancer.create();23 }24 /*25 * 回调方法26 * intercept是拦截之意,此处是指使用代理方法proxy来调用原类(obj)中的指定方法(method),带参数(args),27 */28 @Override29 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {30 proxy.invokeSuper(obj, args);31 return null;32 }33 }
测试类:Clienter
1 public class Clienter { 2 public static void main(String[] args) { 3 /* 4 * Cgliber可以看成是一个带有类生成能力的方法回调器 5 */ 6 Cgliber cgliber = new Cgliber(); 7 /* 8 * 使用方法回调器的类生成功能生成代理类实例:mayunCgliber 9 * 由于getInstance方法返回值为Object类型,并不是MaYun类的子类型,所以需要将其强转为MaYun类型,实际上mayunCgliber是代理实例10 */11 MaYun mayunCgliber = (MaYun) cgliber.getInstance(new MaYun());12 mayunCgliber.quanli();13 }14 }
执行结果:
我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务
使用CGLIB的关键就在方法回调器类Cgliber中:
(1)实现MethodInterceptor接口,该接口中只有一个intercept方法,这个方法的作用就是拦截和回调,拦截的意思是我们可以在具体执行方法的前、后加上其他的代码来扩展功能,回调之意为在这个方法中调用父类(被代理类)中的指定方法。
(2)为了生存代理类及其实例,需要在Cgiber中增加创建代理类的代码,使用CGLIB提供的API来进行类生成,CGLIB使用Enhancer作为类生成器,使用其下的各个接口就能完成类和实例的创建。
其实在测试类中我们可以发现一点,无论是JDK的动态代理还是CGLIB的动态代理,代理类与被代理类之间是存在耦合的,这与静态代理一致,这个耦合提现在方法的调用之上。