1. 详解java动态代理机制以及使用场景
  2. https://blog.csdn.net/u011784767/article/details/78281384

深入理解java动态代理的实现机制

https://blog.51cto.com/4247649/2109130

  1. (1)什么是代理?
  2. 大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。
  3. (2)什么情况下使用代理?
  4. (1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
  5. (2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。
  6. (3)SpringAOP机制就是采用动态代理的机制来实现切面编程。
  7. (3)静态代理和动态代理
  8. 我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。
  9. (4)静态代理
  10. 我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。
  11. /**
  12. * 顶层接口
  13. * @author yujie.wang
  14. *
  15. */
  16. public interface Person {
  17. public void sayHello(String content, int age);
  18. public void sayGoodBye(boolean seeAgin, double time);
  19. }
  20. /**
  21. * 需要被代理的类 实现了一个接口Person
  22. * @author yujie.wang
  23. *
  24. */
  25. public class Student implements Person{
  26. @Override
  27. public void sayHello(String content, int age) {
  28. // TODO Auto-generated method stub
  29. System.out.println("student say hello" + content + " "+ age);
  30. }
  31. @Override
  32. public void sayGoodBye(boolean seeAgin, double time) {
  33. // TODO Auto-generated method stub
  34. System.out.println("student sayGoodBye " + time + " "+ seeAgin);
  35. }
  36. }
  37. /**
  38. * 静态代理,这个代理类也必须要实现和被代理类相同的Person接口
  39. * @author yujie.wang
  40. *
  41. */
  42. public class ProxyTest implements Person{
  43. private Person o;
  44. public ProxyTest(Person o){
  45. this.o = o;
  46. }
  47. public static void main(String[] args) {
  48. // TODO Auto-generated method stub
  49. //s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
  50. Student s = new Student();
  51. //创建代理类对象
  52. ProxyTest proxy = new ProxyTest(s);
  53. //调用代理类对象的方法
  54. proxy.sayHello("welcome to java", 20);
  55. System.out.println("******");
  56. //调用代理类对象的方法
  57. proxy.sayGoodBye(true, 100);
  58. }
  59. @Override
  60. public void sayHello(String content, int age) {
  61. // TODO Auto-generated method stub
  62. System.out.println("ProxyTest sayHello begin");
  63. //在代理类的方法中 间接访问被代理对象的方法
  64. o.sayHello(content, age);
  65. System.out.println("ProxyTest sayHello end");
  66. }
  67. @Override
  68. public void sayGoodBye(boolean seeAgin, double time) {
  69. // TODO Auto-generated method stub
  70. System.out.println("ProxyTest sayHello begin");
  71. //在代理类的方法中 间接访问被代理对象的方法
  72. o.sayGoodBye(seeAgin, time);
  73. System.out.println("ProxyTest sayHello end");
  74. }
  75. }
  76. 测试代码输出:
  77. ProxyTest sayHello begin
  78. student say hellowelcome to java 20
  79. ProxyTest sayHello end
  80. ******
  81. ProxyTest sayHello begin
  82. student sayGoodBye 100.0 true
  83. ProxyTest sayHello end
  84. 静态代理看起来是比较简单的,没有什么问题只不过是在代理类中引入了被代理类的对象而已。
  85. 那么接下来我们看看动态代理。
  86. (5)动态代理
  87. 我们先直接上动态代理的代码,之后再分析代码的行为,上面的Person接口和Student被代理类保持不变。
  88. /**
  89. * 动态代理,动态代理类不要显示的实现被代理类所实现的接口
  90. * @author yujie.wang
  91. *
  92. */
  93. public class MyInvocationHandler implements InvocationHandler{
  94. private Object object;
  95. public MyInvocationHandler(Object object){
  96. this.object = object;
  97. }
  98. @Override
  99. public Object invoke(Object proxy, Method method, Object[] args)
  100. throws Throwable {
  101. // TODO Auto-generated method stub
  102. System.out.println("MyInvocationHandler invoke begin");
  103. System.out.println("proxy: "+ proxy.getClass().getName());
  104. System.out.println("method: "+ method.getName());
  105. for(Object o : args){
  106. System.out.println("arg: "+ o);
  107. }
  108. //通过反射调用 被代理类的方法
  109. method.invoke(object, args);
  110. System.out.println("MyInvocationHandler invoke end");
  111. return null;
  112. }
  113. public static void main(String [] args){
  114. //创建需要被代理的类
  115. Student s = new Student();
  116. //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
  117. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
  118. //获得加载被代理类的 类加载器
  119. ClassLoader loader = Thread.currentThread().getContextClassLoader();
  120. //指明被代理类实现的接口
  121. Class<?>[] interfaces = s.getClass().getInterfaces();
  122. // 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
  123. MyInvocationHandler h = new MyInvocationHandler(s);
  124. //生成代理类
  125. Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
  126. //通过代理类调用 被代理类的方法
  127. proxy.sayHello("yujie.wang", 20);
  128. proxy.sayGoodBye(true, 100);
  129. System.out.println("end");
  130. }
  131. }
  132. 运行测试代码输出如下结果:
  133. MyInvocationHandler invoke begin
  134. proxy: com.sun.proxy.$Proxy0
  135. method: sayHello
  136. arg: yujie.wang
  137. arg: 20
  138. student say helloyujie.wang 20
  139. MyInvocationHandler invoke end
  140. MyInvocationHandler invoke begin
  141. proxy: com.sun.proxy.$Proxy0
  142. method: sayGoodBye
  143. arg: true
  144. arg: 100.0
  145. student sayGoodBye 100.0 true
  146. MyInvocationHandler invoke end
  147. end
  148. 仔细分析上面的动态代理实现代码,我们看到这里涉及到java反射包下的一个接口InvocationHandler和一个类Proxy
  149. package java.lang.reflect;
  150. public interface InvocationHandler {
  151. public Object invoke(Object proxy, Method method, Object[] args)
  152. throws Throwable;
  153. }
  154. 这个接口只有一个invoke方法,我们在通过代理类调用被代理类的方法时,最终都会委托给这个invoke方法执行,
  155. //通过代理类调用 被代理类的方法
  156. proxy.sayHello("yujie.wang", 20);
  157. proxy.sayGoodBye(true, 100);
  158. 所以我们就可以在这个invoke方法中对被代理类进行增强或做一些其他操作。
  159. Proxy类的public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法内部通过拼接字节码的方式来创建代理类,后面我会反编译出它所创建的代理类看看内容。
  160. 我们看这个方法的三个参数:
  161. ClassLoader loader:指定一个动态加载代理类的类加载器
  162. Class<?>[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法。
  163. InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
  164. 你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。
  165. (6)反编译Proxy.newProxyInstance所创建的代理类
  166. //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
  167. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
  168. 我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class
  169. 通过jd-gui反编译代码如下,其中注释是我加上去的:
  170. package com.sun.proxy;
  171. import com.yujie.proxy.dynamic.Person;
  172. import java.lang.reflect.InvocationHandler;
  173. import java.lang.reflect.Method;
  174. import java.lang.reflect.Proxy;
  175. import java.lang.reflect.UndeclaredThrowableException;
  176. /**
  177. *代理类也实现了Person接口,看起来和静态代理的方式也会一样的
  178. *同时代理类也继承了Proxy类
  179. */
  180. public final class $Proxy0 extends Proxy implements Person{
  181. private static Method m4;
  182. private static Method m1;
  183. private static Method m0;
  184. private static Method m3;
  185. private static Method m2;
  186. public $Proxy0(InvocationHandler paramInvocationHandler)
  187. throws
  188. {
  189. super(paramInvocationHandler);
  190. }
  191. //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
  192. public final void sayGoodBye(boolean paramBoolean, double paramDouble)
  193. throws
  194. {
  195. try
  196. {
  197. // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
  198. // m4为代理类通过反射获得的Method
  199. this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) });
  200. return;
  201. }
  202. catch (Error|RuntimeException localError)
  203. {
  204. throw localError;
  205. }
  206. catch (Throwable localThrowable)
  207. {
  208. throw new UndeclaredThrowableException(localThrowable);
  209. }
  210. }
  211. public final boolean equals(Object paramObject)
  212. throws
  213. {
  214. try
  215. {
  216. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  217. }
  218. catch (Error|RuntimeException localError)
  219. {
  220. throw localError;
  221. }
  222. catch (Throwable localThrowable)
  223. {
  224. throw new UndeclaredThrowableException(localThrowable);
  225. }
  226. }
  227. public final int hashCode()
  228. throws
  229. {
  230. try
  231. {
  232. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  233. }
  234. catch (Error|RuntimeException localError)
  235. {
  236. throw localError;
  237. }
  238. catch (Throwable localThrowable)
  239. {
  240. throw new UndeclaredThrowableException(localThrowable);
  241. }
  242. }
  243. //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
  244. public final void sayHello(String paramString, int paramInt)
  245. throws
  246. {
  247. try
  248. {
  249. // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
  250. // m4为代理类通过反射获得的Method
  251. this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) });
  252. return;
  253. }
  254. catch (Error|RuntimeException localError)
  255. {
  256. throw localError;
  257. }
  258. catch (Throwable localThrowable)
  259. {
  260. throw new UndeclaredThrowableException(localThrowable);
  261. }
  262. }
  263. public final String toString()
  264. throws
  265. {
  266. try
  267. {
  268. return (String)this.h.invoke(this, m2, null);
  269. }
  270. catch (Error|RuntimeException localError)
  271. {
  272. throw localError;
  273. }
  274. catch (Throwable localThrowable)
  275. {
  276. throw new UndeclaredThrowableException(localThrowable);
  277. }
  278. }
  279. static
  280. {
  281. try
  282. {//代理类通过反射 获得的接口方法Method
  283. m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });
  284. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  285. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  286. //代理类通过反射 获得的接口方法Method
  287. m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });
  288. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  289. return;
  290. }
  291. catch (NoSuchMethodException localNoSuchMethodException)
  292. {
  293. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  294. }
  295. catch (ClassNotFoundException localClassNotFoundException)
  296. {
  297. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  298. }
  299. }
  300. }
  301. 总结一下:
  302. jdk的代理让我们在不直接访问某些对象的情况下,通过代理机制也可以访问被代理对象的方法,这种技术可以应用在很多地方比如RPC框架,Spring AOP机制,但是我们看到jdk的代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重新那些方法。这样一个没有实现任何接口的类就无法通过jdk的代理机制进行代理,当然解决方法是使用cglib的代理机制进行代理。

详解java动态代理机制以及使用场景的更多相关文章

  1. 详解Java动态代理机制

    之前介绍的反射和注解都是Java中的动态特性,还有即将介绍的动态代理也是Java中的一个动态特性.这些动态特性使得我们的程序很灵活.动态代理是面向AOP编程的基础.通过动态代理,我们可以在运行时动态创 ...

  2. 详解Java动态代理机制(二)----cglib实现动态代理

    上篇文章的结尾我们介绍了普通的jdk实现动态代理的主要不足在于:它只能代理实现了接口的类,如果一个类没有继承于任何的接口,那么就不能代理该类,原因是我们动态生成的所有代理类都必须继承Proxy这个类, ...

  3. Java 动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  4. 大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解

    代理模式 Java动态代理运用了设计模式中常用的代理模式 代理模式: 目的就是为其他对象提供一个代理用来控制对某个真实对象的访问 代理类的作用: 为委托类预处理消息 过滤消息并转发消息 进行消息被委托 ...

  5. Java 动态代理机制分析及扩展

    Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ...

  6. [转]Java 动态代理机制分析及扩展

    引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执 ...

  7. 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)

    在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...

  8. java动态代理机制

    首先了解代理设计模式,其思想是为其他对象提供一种代理以控制对这个对象的访问. java动态代理就是遵循这种思想,spring中的AOP实现原理就是java的动态代理. 在java的动态代理机制中,有两 ...

  9. Java 动态代理机制分析及扩展--转

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...

随机推荐

  1. Windows(win2016、win2019、win10)在IIS下添加.NET Framework 3.5 NetFx3 失败 (状态为:0x800f0950)的解决办法

    今天一个客户自己的电脑安装了一个windows server 2016 想装一个IIS,程序一个C+写的ERP,NET是必然,NET4.7可以安装了,但就是3.5,如何也装不上,错误(状态为:0x80 ...

  2. In Vitro model验证 | Harnessing single-cell genomics to improve the physiological fidelity of organoid-derived cell types

    Transcriptional benchmarking of in vitro cells to in vivo with single-cell rna-seq - 简介 Harnessing s ...

  3. TFS变更地址

    本文链接:https://blog.csdn.net/qq_31117007/article/details/78044381 1: 今天公司服务器换了IP地址,然后发现tfs的服务器删除不了,也添加 ...

  4. 避免git clone和push时每次都需要输入用户名和密码

    有三种方式解决git clone时每次都需要输入用户名和密码, 1. SSH免密方式 使用git bash ssh-keygen或puttygen.exe生成公钥. 2. 配置全局开机存储认证信息 下 ...

  5. mybatis ResultHandler vs ResultSetHandler及自定义扩展

    ResultSetHandler是mybatis的关键类之一,用于对jdbc返回的ResultSet进行映射处理,其中包括列前缀处理,逻辑分页,鉴别器(Discriminator,基于值实现动态映射列 ...

  6. 安全漏洞XSS、CSRF、SQL注入以及DDOS攻击

    随着互联网的普及,网络安全变得越来越重要,程序员需要掌握最基本的web安全防范,下面列举一些常见的安全漏洞和对应的防御措施. 0x01: XSS漏洞 1.XSS简介 跨站脚本(cross site s ...

  7. Manifest中meta-data扩展元素数据的配置与获取

    简介-meta是什么 在AndroidManifest.xml清单文件中 我们有时会看到如下类似的<meta-data ... >元素开始的配置内容: <meta-data andr ...

  8. pd.ExcelWriter(to_excel)保存结果到已存在的excel文件中

    网易云课堂该课程链接地址 https://study.163.com/course/courseMain.htm?share=2&shareId=400000000398149&cou ...

  9. Spring-boot +Shiro 导致事务无效

    今天在开发过程中,遇到一个情况,就是事务事务,同项目的别的service都在事务中,可以就是有一个事务失效. 排除了各种情况 1.检查数据库的引擎是否是innoDB 2.方法是否为public 3.这 ...

  10. JS的base64编码解码

    Unicode问题解法 有个小坑是它只支持ASCII. 如果你调用btoa("中文")会报错: Uncaught DOMException: Failed to execute ' ...