JavaSE基础知识(十七)--Java复用代码之动态代理

news/2024/7/7 9:47:56

Java SE 是什么,包括哪些内容(十七)?

本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!
上一篇博文中详细说明了静态代理的内容,也指出了静态代理只适合小范围的使用(使用和维护都很麻烦),真正强大的是动态代理(使用和维护相对简单)
下面将动态代理的代码展示出来。

1、动态代理模式

代码示例:
⑴、接口

// 接口一
   //接口一:Eat
   public interface Eat{
      //规定了一个行为就是eat()
      void eat();
   }
   //接口二:Walk
   public interface Walk{
      //规定了一个行为就是eat()
      void walk();
   }

⑵、被代理类:

// 分别实现了接口一和接口二,表示拥有了它们的行为或者说是能力
   public class Person implements Eat,Walk{
      //实现接口中的方法eat(),表示具体是一种什么行为
      public void eat(){
         //打印字符串"eat()"
         System.out.prinyln("eat()");
      };
      //实现接口中的方法walk(),表示具体是一种什么行为
      public void walk(){
         //打印字符串"walk()"
         System.out.prinyln("walk()");
      };
   }

⑶、调用处理器(这个调用在这里是名词)实现固定的接口 InvocationHandler
它可以在Java的帮助文档中找到:
Java帮助文档中的接口InvocationHandler!
方法详细信息!
代码示例:

// 调用处理器
   //类MyHandler实现了接口InvocationHandler,这是动态代理的硬性规定。就得这么来
   //类似,如果你想拥有比较的能力,就得实现Comparable接口等
   public class MyHandler implements InvocationHandler{
      //被代理类对象的实例(在这个示例中指的就是被代理类Person的实例)
      private Object obj;
      //构造方法,带一个Object类型的形式参数
      public MyHandler(Object obj){
         //对象初始化(赋值)
         this.obj = obj;
      }
      //方法invoke,形式参数分别是
      //Obeject类型的proxy(代表的是代理类,这个参数有点复杂,我后面会详细说,这里暂时用不上)
      //Method类型的method(代表的是运行时调用的那个方法)
      //Object数组类型的args(代表的是运行时调用的那个方法的实际参数,可能一个或者多个,
      //所以是数组类型)
      public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
         //可能还有其他的代码...
         //通过Java的反射执行方法method(如果这里不明白,可以去找到我有关反射的博文,
         //了解如何通过反射执行一个方法).
         method.invoke(obj, args);
         //可能还有其他的代码...
         //返回null
         return null;
      }
   }

⑷、利用类Proxy生成某一接口的动态代理对象
它可以在Java的帮助文档中找到:
Java帮助文档中的类Proxy!
注意划红线的地方:生成某一接口的代理!动态代理是代理接口了!不是类!
动态代理的注意事项!
代码示例:

// 生成某一接口的代理对象,注意,我这里写的是接口的代理对象,不是类的代理对象!!!
   //代理类Myproxy,在这里代理的是接口Eat,Walk(同时代理!)
   public class Myproxy{
      //方法getProxy(Object obj),形式参数为Object类型的obj,表示需要传入一个
      //被代理对象的实例生成代理对象并返回
      public Object getProxy(Object obj){
         //通过类Proxy的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法生成一个代理对象实例
         //它有三种类型的形式参数,分别是
         //ClassLoader类型的loader(表示代理类的类加载器,可以指定一个具体的类加载器对象,也可以传入一个null,表示使用默认的类加载器)
         //Class<?>[]类型的interfaces(表示被代理类实现的所有接口
         //(在这里就表示接口Eat和接口Walk),所以是数组类型)
         //InvocationHandle类型的h(表示调用处理器,在这里就是MyHandler)
         //返回代理类的对象实例
         return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
         obj.getClass().getInterfaces(), new MyHandler(obj));
         //注意,这个方法getProxy(Object obj)中的形式参数和类MyHandler中构造方法的形式参数
         //指的是同一个对象,都是被代理类的对象.
         //所以在这里直接将obj作为实际参数传入了类MyHandler中进行初始化。
      }
   }

⑸、测试

// 动态代理类测试
   //类Test
   public class Test{
      //程序执行入口main方法
      public static void main(String[] args){
         //创建被代理类Person的对象
         Person person = new Person();
         //在第四步的时候就强调了,类Proxy生成的是接口的代理对象,并不是被代理类的代理对象,
         //也就是说Proxy生成的代理是代理接口,并不是代理类。
         //所以等号左边应该是接口类型,而不应该是一个具体的类(应该是Eat、Walk,而不是Person)
         //通过类Myproxy的getProxy(Object obj)方法返回一个接口代理对象实例.然后赋值给
         //具体的接口类型
         //注意,这里需要进行类型的强制转换,如果不强制转换,它仅仅是一个Object类型
         Eat e = (Eat)new Myproxy().getProxy(person);
         //调用具体的方法
         e.eat();   
         //返回接口Walk的代理对象实例
         Walk w = (Walk)new Myproxy().getProxy(person);
         w.walk();
      }
   }

结果示例:
执行结果!

2、静态代理模式与动态代理模式的对比

静态代理模式总共分为步:
①、接口
②、被代理类
③、代理类
④、测试

一期开发的时候,所有的代码都已经确定了。
结果现在是二期,你突然发现需要增加两个接口,我们来看一下需要改动哪些地方:
①、接口:编写你新增的两个接口。
②、被代理类:在它已实现的接口上增加新增的接口,并实现新增接口中的所有方法。
③、代理类:在它已实现的接口上增加新增的接口,并实现新增接口中的所有方法。
④、测试:按需新增测试代码。
如果有100个、甚至1000个被代理类,那么就肯定需要100个、甚至1000个代理类。剩下的的,就不需要我多说什么了…
有兴趣可以自己去尝试!
动态代理模式总共分为步:
①、接口
②、被代理类
③、调用处理器
④、接口代理类
⑤、测试

一期开发的时候,所有的代码都已经确定了。
结果现在是二期,你突然发现需要增加两个接口,我们来看一下需要改动哪些地方:
①、接口:编写你新增的两个接口。
②、被代理类:在它已实现的接口上增加新增的接口,并实现新增接口中的所有方法。
③、调用处理器:不需要改动
④、接口代理类:不需要改动
⑤、测试:按需新增测试代码。
你会发现动态代理模式真正做到了让你只聚焦核心的业务,因为新增接口被代理类去实现接口都是你必须要做的核心业务。是不能省略的。
动态代理模式改动图示:
1、新增两个接口:
新增两个接口!
2、被代理类实现新增的两个接口:
被代理类实现新增的两个接口!
3、调用处理器:
不用做任何改动!
4、代理类:
不用做任何改动!
5、测试
测试没有任何问题!
测试没有任何问题!

3、动态代理模式的核心思想:

其实,静态代理模式和动态代理模式的根本区别就在于:
静态代理模式在编译期就已经确定好了!
动态代理模式在编译期还未确定,而是根据运行期对方法的调用再利用反射找到目标方法
静态代理模式流程图:

代理类 被代理类 接口 使用的时候,直 接创建代理类的 对象,再通过对 象调用目标方法! 实现接口 实现接口 添加增强代码 代理类 被代理类 接口

在这种模式下,在编译期就能确定代理类有哪些方法,需要使用的时候,直接创建代理类的对象,然后调用方法(有什么方法就调用什么方法)。实际上就是正常地创建类的对象实例,然后调用它的方法。
动态代理模式流程图:

代理类 接口 被代理类 InvocationHandler 代理接口,不代理具体的类 实现接口 找到方法的具体实现 通过反射调用 代理类 接口 被代理类 InvocationHandler

动态代理模式就稍微有点复杂了。
首先,代理类代理的是接口,并不是具体的类!
其次,它根据运行期的方法调用进行代理!
为什么说是运行期呢?
因为,它的流程是这样的:
根据你运行时候具体调用的方法(也就是main方法里面调用的方法):
比如:
运行期!
这里调用的是方法run(),根据这个方法名称,进入代理类内(代理类内生成代理类的实例并返回),根据传入的参数(接口数组–obj.getClass().getInterfaces(),调用处理器–new MyHandler(obj))检查是否有run()这个接口方法,如果有,再进入调用处理器,通过Java的反射执行被代理类的具体方法。
静态代理模式类似分散的管理,需要的时候加进来,然后一个一个地用(一个代理类对应一个被代理类)。
动态代理模式类似集中管理,将需要的能力加进库里面来,直接就能使用(一个代理类对应多个接口,想用哪个接口,就利用代理类生成哪个接口的代理实例)!
下一篇博文将介绍调用处理器中那个Object类型形式参数proxy。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正


http://www.niftyadmin.cn/n/1982546.html

相关文章

JavaSE基础知识(十七)--Java动态代理中InvocationHandler中Object类型参数proxy的作用

Java SE 是什么&#xff0c;包括哪些内容(十七)&#xff1f; 本文内容参考自Java8标准 再次感谢Java编程思想对本文的启发&#xff01; 上一篇博文中详细说明了动态代理的内容&#xff0c;但是在说到调用处理器InvocationHandler的时候&#xff0c;有一个Object类型的参数prox…

JavaSE基础知识(十七)--Java复用代码之结合使用组合与继承(正确的初始化与清理)

Java SE 是什么&#xff0c;包括哪些内容(十七)&#xff1f; 本文内容参考自Java8标准 再次感谢Java编程思想对本文的启发&#xff01; 在我们的日常编程工作中&#xff0c;同时使用组合与继承是很常见的事情&#xff0c;下面通过一个例子来说明&#xff1a; PS&#xff1a;同…

基于HTML5实现3D热图Heatmap应用

为什么80%的码农都做不了架构师&#xff1f;>>> Heatmap热图通过众多数据点信息&#xff0c;汇聚成直观可视化颜色效果&#xff0c;热图已广泛被应用于气象预报、医疗成像、机房温度监控等行业&#xff0c;甚至应用于竞技体育领域的数据分析。 http://www.hightopo…

JavaSE基础知识(十七)--Java复用代码之在组合与继承之间选择

Java SE 是什么&#xff0c;包括哪些内容(十七)&#xff1f; 本文内容参考自Java8标准 再次感谢Java编程思想对本文的启发&#xff01; 通过前面的博文&#xff0c;我们了解到&#xff0c;组合与继承都允许在新的类中放置子对象&#xff0c;组合是显式地这么做&#xff0c;继承…

使用Hexo搭建自己的博客

2019独角兽企业重金招聘Python工程师标准>>> 参考文章 使用GitHub和Hexo搭建免费静态Blog http://wsgzao.github.io/post/hexo-guide 我的主题 https://github.com/ppoffice/hexo-theme-icarus hexo博客换主题--icarus http://hexo.trity.cc/2015/08/24/hexo%…

JavaSE基础知识(十七)--Java复用代码之关键字protected的详细描述

Java SE 是什么&#xff0c;包括哪些内容(十七)&#xff1f; 本文内容参考自Java8标准 再次感谢Java编程思想对本文的启发&#xff01; 在博文"JavaSE基础知识(十六)–Java的访问权限控制关键字(public、protected、private)"中略微提及了关键字protected&#xff0…

.xyz域名总量TOP10:西部数码第四 排名升1位

IDC评述网&#xff08;idcps.com&#xff09;10月16日报道&#xff1a;根据ntldstats.com发布的最新数据显示&#xff0c;截止至2015年10月15日17时&#xff0c;国内外.xyz域名总量十强排名情况&#xff0c;相比上期9月28日&#xff0c;有所变动。西部数码、阿里云&#xff08;…

JavaSE基础知识(十七)--Java复用代码之关键字final的详细描述

Java SE 是什么&#xff0c;包括哪些内容(十七)&#xff1f; 本文内容参考自Java8标准 再次感谢Java编程思想对本文的启发&#xff01; 从某个方面来讲&#xff0c;关键字final的作用与关键字protected的作用是相反的。 根据上下文环境&#xff0c;Java的关键字final的含义和作…