[关闭]
@liayun 2016-09-20T16:32:53.000000Z 字数 4913 阅读 1370

动态代理(一)——动态代理入门

java基础加强


动态代理简单入门

动态代理技术在实际开发中用到的非常多,所以有必要详解一下这门技术。这门技术非常难,因此学起来还是比较困难的,但我们不怕困难,定要啃下这块硬骨头。
要想理解动态代理这门技术,必须明确两个概念:

现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。那么如何编写生成代理对象的类呢?
要想编写生成代理对象的类,必须考虑到两个要素:

Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

  1. 生成代理对象使用哪个类装载器
  2. 生成哪个对象的代理对象,通过接口指定
  3. 生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定

初学者必须理解,或不理解必须记住的2件事情:

相关概念看完之后,大家可能还是处在云里雾里中,但没关系,我们接下来通过一个例子来领大家入门。
李宇春,网上称春哥(在此处我无意诋毁她),她在还没成名前,一些大老板可能会点她唱歌或跳舞,那么她就会自己去给这些金主唱歌或跳舞,可能是有些许迫不得已吧!但是她成名之后,就会有自己的代理人,即经纪人,一些金主点她唱歌或跳舞时,其代理人就会跳出来,拦截对李宇春的访问,这个时候由代理人出面,若要我代理的对象——李宇春唱歌,必须给1万美刀;若要我代理的对象——李宇春跳舞,必须给2万美刀。这个场景用代码来描述就应该是这样。
首先设计真实的业务对象,即李宇春。

  1. public class Liyuchun {
  2. public void sing() {
  3. System.out.println("春哥唱歌了!!");
  4. }
  5. public void dance() {
  6. System.out.println("春哥跳舞了!!");
  7. }
  8. }

接下来我们就要编写生成代理对象的类,首先明确其要代理的对象是李宇春,接着就要设计一个方法生成代理对象(即代理人)。

  1. public class LiyuchunProxy {
  2. private Person chunchun = new Liyuchun();
  3. public Xxx(返回值) createProxy() {
  4. return ...
  5. }
  6. }

这儿,我们就要思考生成代理对象的方法——createProxy()的返回值的类型应该是什么了?不可能是李宇春,即Liyuchun,那到底应该是什么类型呢?在Java里面有这样一个约定:要想创建某一个对象的代理对象,那么该对象必须实现一个接口
由于李宇春是一个人,所以我们抽出一个的接口。

  1. public interface Person {
  2. void sing();
  3. void dance();
  4. }

真实的业务对象(即李宇春)的代码就应修改为:

  1. public class Liyuchun implements Person {
  2. @Override
  3. public void sing() {
  4. System.out.println("春哥唱歌了!!");
  5. }
  6. @Override
  7. public void dance() {
  8. System.out.println("春哥跳舞了!!");
  9. }
  10. }

那这样我们就明确了生成代理对象的方法——createProxy()的返回值的类型了,即为Person,通过该方法产生李宇春的代理,李宇春的代理同样也是一个人,对不对?接下来,我们使用Java提供了的Proxy类,调用它的newInstance方法生成李宇春的代理对象(即代理人),代码大概是这样:

  1. public class LiyuchunProxy {
  2. private Person chunchun = new Liyuchun();
  3. public Person createProxy() {
  4. // 产生某个对象的代理对象
  5. return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
  6. /*
  7. * proxy:把代理对象自身传递进来
  8. * method:代表当前调用的方法
  9. * args:当前调用方法的参数
  10. */
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. 在这里面编写代码来指定产生的代理对象干什么事情...
  14. return null;
  15. }
  16. });
  17. }
  18. }

假设这样的生成代理对象的类编写好了,有些金主来点春哥唱歌或跳舞,那么其代理人拦截下来之后,就调用代理人自己的唱歌或跳舞方法。这样代码就是这样的:

  1. public class Demo1 {
  2. public static void main(String[] args) {
  3. LiyuchunProxy proxy = new LiyuchunProxy();
  4. Person person = proxy.createProxy();
  5. person.sing();
  6. person.dance();
  7. }
  8. }

只要调用代理对象的唱歌、跳舞方法,实际上都是调用invoke方法里面的代码,即invoke方法由唱歌、跳舞方法来调用,当唱歌、跳舞方法来调用invoke方法做事情的时候,会给invoke方法传递一些参数进来。这说明代理对象到底干什么事情是由invoke方法里面的代码来决定的
这样我们再回过头来彻底编写完生成代理对象的类。

  1. public class LiyuchunProxy {
  2. private Person chunchun = new Liyuchun();
  3. public Person createProxy() {
  4. // 产生某个对象的代理对象
  5. return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
  6. /*
  7. * proxy:把代理对象自身传递进来
  8. * method:代表当前调用的方法
  9. * args:当前调用方法的参数
  10. */
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. // 在这里面编写代码来指定产生的代理对象干什么事情
  14. String methodName = method.getName();
  15. if (methodName.equals("sing")) {
  16. System.out.println("拿1万刀来!!!");
  17. method.invoke(chunchun, args); // 春春唱歌了
  18. } else if (methodName.equals("dance")) {
  19. System.out.println("拿2万刀来!!!");
  20. method.invoke(chunchun, args); // 春春跳舞了
  21. } else {
  22. System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
  23. }
  24. return null;
  25. }
  26. });
  27. }
  28. }

动态代理进阶

动态代理技术怎可能像上面那样简单呢?动态代理入门之后,我们再来看看还算是比较复杂的动态代理。
金主除了点春哥唱歌之外,还要指定她专门唱哪首歌,春哥唱完歌之后还要对金主说一声谢谢;他们除了点春哥跳舞之外,还要指定她专门跳什么舞,春哥跳完舞之后还得给金主们一个飞吻!在这样的场景下,Person接口的代码应修改为:

  1. public interface Person {
  2. String sing(String name);
  3. String dance(String name);
  4. }

真实业务对象(即李宇春)的代码应修改为:

  1. public class Liyuchun implements Person {
  2. @Override
  3. public String sing(String name) {
  4. System.out.println("春哥唱"+name+"歌了!!");
  5. return "谢谢哟!!!";
  6. }
  7. @Override
  8. public String dance(String name) {
  9. System.out.println("春哥跳"+name+"舞了!!");
  10. return "飞吻!!!";
  11. }
  12. }

生成代理对象的类的代码应修改为:

  1. public class LiyuchunProxy {
  2. private Person chunchun = new Liyuchun();
  3. public Person createProxy() {
  4. // 产生某个对象的代理对象
  5. return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
  6. /*
  7. * proxy:把代理对象自身传递进来
  8. * method:代表当前调用的方法
  9. * args:当前调用方法的参数
  10. */
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. // 在这里面编写代码来指定产生的代理对象干什么事情
  14. String methodName = method.getName();
  15. if (methodName.equals("sing")) {
  16. System.out.println("拿1万刀来!!!");
  17. /*
  18. * 春春要唱歌了,唱完歌还要对金主们说声:谢谢哟!!!
  19. * 由于是代理对象拦截了对真实业务对象的访问,所以若是不将"谢谢哟!!!"返回,
  20. * 结果就是其返回给代理对象了,金主们肯定要不爽了。
  21. */
  22. return method.invoke(chunchun, args);
  23. } else if (methodName.equals("dance")) {
  24. System.out.println("拿2万刀来!!!");
  25. /*
  26. * 春春要跳舞了,跳完舞还要给金主们一个飞吻!!!
  27. * 同样,若是不将"飞吻!!!"返回,
  28. * 结果就是其返回给代理对象了,金主们肯定要日娘了。
  29. */
  30. return method.invoke(chunchun, args);
  31. } else {
  32. System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
  33. }
  34. return null;
  35. }
  36. });
  37. }
  38. }

这样,金主要点春哥唱《江南皮革厂》,并且还要我们的春哥跳钢管舞,那么代码就该是这样的:

  1. public class Demo {
  2. public static void main(String[] args) {
  3. LiyuchunProxy proxy = new LiyuchunProxy();
  4. Person person = proxy.createProxy();
  5. String result = person.sing("江南皮革厂");
  6. System.out.println(result);
  7. result = person.dance("钢管舞");
  8. System.out.println(result);
  9. }
  10. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注