1. 代理就是中介,中间人。法律上也有代理,比如代理律师之类,委托人将自己的一部分权限委托给代理者,
  2. 代理者就拥有被代理者(委托人)的部分权限,并且可以以被代理人的名义来实行这些权限,此时代理者
  3. 与委托人等同,当然代理人也可以在实行权限时配合自己的能力来进行,当然不能超出这个权限。
  4. Java中的代理模式类似于上面的代理,也是为一个类(委托类)创建一个代理类,来代表它来对外提供功能。
  5. 如何在Java中创建一个类的代理类呢?
  6. 很简单,我们需要创建一个公共接口,委托类要实现这个接口,再创建一个接口的实现类作为代理类,
  7. 在这个类中的方法中可以直接调用委托类中的同名方法,外部类要进行访问时,可以使用接口指向代理类实例,
  8. 调用代理类中的方法,从而间接调用委托类中的具体方法实现。

静态代理

以法律上的委托代理为例
总接口: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. }

代理律师:LvShi

  1. public class LvShi implements ZiRanRen {
  2. @Override
  3. public void Quanli() {
  4. new MaYun().Quanli();
  5. }
  6. }

测试类:Clienter

  1. public class Clienter {
  2. public static void main(String[] args) {
  3. ZiRanRen ls = new LvShi();
  4. ls.Quanli();
  5. }
  6. }
  7. 执行结果
  8. 我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务
  9. 可以看出,我们想对外开放某些功能,就可以将这些功能在代理类中被引用,如此一来,
  10. 屏蔽了我们不想外露的功能,只将我们想开放的功能开放出来。
  11. 即委托类中其实是可以有很多方法的,很多功能的,我们可以酌情对外开放,
  12. 代理类犹如一道大门,将委托类与外部调用者隔绝开来,只将部分功能赋予这个大门,
  13. 来代替委托类行使这个功能,哪怕最终还是要牵扯到自身(因为最终还是要调用委托类的对应方法实现)。

总结

  1. 代理模式很简单,只要记住以下关键点,简单易实现:
  2. 1)代理类与委托类实现同一接口
  3. 2)在委托类中实现功能,在代理类的方法中中引用委托类的同名方法
  4. 3)外部类调用委托类某个方法时,直接以接口指向代理类的实例,这正是代理的意义所在:屏蔽。
  5. 代理模式场景描述:
  6. 1)当我们想要隐藏某个类时,可以为其提供代理类
  7. 2)当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现
  8. (代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中进行权限判断
  9. 来进行不同权限的功能调用)
  10. 3)当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展
  11. (只针对简单扩展,可在引用委托类的语句之前与之后进行)
  12. 代理模式虽然实现了调用者与委托类之间的强耦合,但是却增加了代理类与委托类之间的强耦合
  13. (在代理类中显式调用委托类的方法),而且增加代理类之后明显会增加处理时间,拖慢处理时间。

动态代理

  1. 为了解决上面静态代理的缺陷,动态代理就产生了。
  2. 动态代理有两种方式,一种是JDK实现的动态代理,一种是CGLIB实现的动态代理,
  3. 其中JDK动态代理是基于接口的,CGLIB是基于类的,明显CGLIB要高效一些,因为它免去了接口的消耗与限制。

JDK动态代理

JDK动态代理是基于接口实现的,采用的是反射原理。

  1. 要使用JDK动态代理需要实现InvocationHandler接口,InvocationHandler接口是JDK专为
  2. 动态代理而设计的接口,其有数个实现类,是JDK中使用动态代理来实现一些其他的功能,
  3. 我们的实现类似于它们,完全可以参考其代码。
  4. InvocationHandler接口中只有一个方法invoke,这个方法用于调用具体的实现来完成功能,
  5. 调用的过程需要借助于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. public class MyInvocationHandler implements InvocationHandler{
  4. private Object target;
  5. MyInvocationHandler(){super();}
  6. MyInvocationHandler(Object target){
  7. super();
  8. this.target = target;
  9. }
  10. @Override
  11. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12. if("quanli".equals(method.getName())){
  13. return method.invoke(target, args);
  14. }else
  15. return method.invoke(target, args);
  16. }
  17. }

测试类:Clienter

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Proxy;
  3. public class Clienter {
  4. public static void main(String[] args) {
  5. //动态代理实现
  6. ZiRanRen ls = new MaYun();
  7. InvocationHandler invocationHandler = new MyInvocationHandler(ls);
  8. ZiRanRen Proxyer = (ZiRanRen)Proxy.newProxyInstance(ls.getClass().getClassLoader(), ls.getClass().getInterfaces(), invocationHandler);
  9. Proxyer.quanli();
  10. }
  11. }
  12. 执行结果
  13. 我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务
  14. 1MyInvocationHandler实现类中定义的Object target中的target指的是接口的实现类,
  15. 即之前所说的委托类,本段代码中指的就是MaYun类,这个从最后测试类中第78行可以看出,
  16. 我们将MyYun类的实例作为参数来生成MyInvocationHandler实例。
  17. 2)代码编写的阶段并未显式调用MaYun类的quanli方法,但是最后却是实实在在被调用了,这时怎么回事呢?
  18. 其实这正是动态代理的魅力所在,动态代理的动态二字的含义是不局限于某一个具体类,
  19. 当我们实现了一个InvocationHandler接口的实例之后,就可以使用这个实例进行任意类的代理,
  20. 被代理的类是在运行时才会确定(这也正是反射的能力之所在),编码阶段根本无从知晓,复用性极强。
  21. 3InvocationHandler接口的三个参数问题:第一个参数proxy表示的是被代理的实例
  22. (此处指MaYun类的实例ls),第二个参数method代表要执行的被代理类中的方法
  23. (此处指MaYun类中的quanli方法),第三个参数表示被代理类中方法的参数列表
  24. (此处指MaYun类的quanli方法的参数列表)。如此看来,InvocationHandler的三个参数
  25. 就是精确指定要执行哪个类的哪个方法,参数是***。当然在代码中还是泛指,这个精确要到
  26. 程序运行时才能真正体现。
  27. 4)测试类中创建代理类实例的模式与静态代理的模式一致,表示与被代理类实现同一接口。
  28. 使用Proxy代理类的静态方法newProxyInstance方法来完成代理类实例的创建,
  29. 其有三个参数:
  30. 第一个是实例类的加载器,
  31. 第二个是实例类实现的接口,
  32. 第三个是InvocationHandler接口的实例。

CGLIB动态代理

  1. CGLIB动态代理不同于JDK提供的动态代理,它不再基于接口,而是直接基于被代理类来完成代理模式。
  2. 我们可以将CGLIB看作是一个代理类的生成工具,通过编程的方式来完成一个类的创建及实例的创建,
  3. 不同于我们以前直接使用Class来创建类的方式,明显复杂性提升很多,但是CGLIB将这种复杂的类生成功能屏蔽化,
  4. 我们只需要借助于它所提供的API就可以完成代理类的生成和实例的创建。
  5. 要使用CGLIB,需要向导入cglibjar包,此处我们使用: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. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. /**
  6. * Cglib就犹如一个代码式的类生成器,一般手动创建类的内容涉及到的东西,都需要通过Cglib提供的API来完成。
  7. * 如下代码中:设置超类和设置回调,对应于正常类编写时,编写的继承类与方法回调
  8. * 这里实现的是MethodInterceptor接口(方法拦截器),这个接口的作用就是实现方法拦截(回调)
  9. *
  10. */
  11. public class Cgliber implements MethodInterceptor{
  12. private Object target;//这个target指的就是被代理类的实例
  13. /*
  14. * 生成代理实例(依据被代理类来生成代理类),Enhancer则是代理类生成工具
  15. */
  16. public Object getInstance(Object target){
  17. this.target = target;
  18. Enhancer enhancer = new Enhancer();
  19. enhancer.setSuperclass(this.target.getClass());//设置被继承类(超类)
  20. enhancer.setCallback(this);//设置回调
  21. return enhancer.create();
  22. }
  23. /*
  24. * 回调方法
  25. * intercept是拦截之意,此处是指使用代理方法proxy来调用原类(obj)中的指定方法(method),带参数(args),
  26. */
  27. @Override
  28. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
  29. 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. }
  15. 执行结果
  16. 我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务
  17. 使用CGLIB的关键就在方法回调器类Cgliber中:
  18. 1)实现MethodInterceptor接口,该接口中只有一个intercept方法,这个方法的作用就是拦截和回调,
  19. 拦截的意思是我们可以在具体执行方法的前、后加上其他的代码来扩展功能,回调之意为在这个方法中调用父类
  20. (被代理类)中的指定方法。
  21. 2)为了生存代理类及其实例,需要在Cgiber中增加创建代理类的代码,使用CGLIB提供的API来进行类生成,
  22. CGLIB使用Enhancer作为类生成器,使用其下的各个接口就能完成类和实例的创建。
  23. 其实在测试类中我们可以发现一点,无论是JDK的动态代理还是CGLIB的动态代理,
  24. 代理类与被代理类之间是存在耦合的,这与静态代理一致,这个耦合体现在方法的调用之上。