相信大家都使用过动态代理,就算没有写过,应该也用过Spring来做过Bean的组织管理。如果使用过Spring,那大多数情况应该已经不知不觉地用到动态代理了。

动态代理中所说的“动态”,是针对使用Java代码实际编写了代理类的“静态”代理而言的,它的优势不在于省去了编写代理类那一点编码工作量,而是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中。

1、话不多说,直接上代码

public class DynamicProxyTest {interface IHello {void sayHello();}static class Hello implements IHello {@Overridepublic void sayHello() {System.out.println("hello world");}}static class DynamicProxy implements InvocationHandler {Object originalObj;Object bind(Object originalObj) {this.originalObj = originalObj;return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("welcome");System.out.println(proxy instanceof IHello);System.out.println(proxy.getClass());return method.invoke(originalObj, args);}}public static void main(String[] args) {// 在项目目录下会生成代理对象的 class 文件System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");IHello hello = (IHello) new DynamicProxy().bind(new Hello());hello.sayHello();}
}

运行 main 方法,当执行 sayHello() 方法时,会进行输出,输出如下:

2、对上面输出的解释

动态代理,使用了字节码生成技术,运行时生成字节码( 也就是 class,class 中的方法由这里获取到 originalObj.getClass().getInterfaces() ),并通过类加载器(类加载器由这里获取到 originalObj.getClass().getClassLoader())将其加载到 JVM 中

生成的代理类 $Proxy0 extends Proxy implements IHello

main 方法中的 hello.sayHello(); 实际上调用的是代理对象的 sayHello 方法,该方法又直接调用 DynamicProxy 类的 invoke 方法(调用的对象是 Proxy.newProxyInstance 方法传入的 this)

3、对代理对象 $Proxy0 到底是什么

读者将上面 class 运行之后会发现一个名为 $Proxy0 的 class 文件,该文件内容如下:

package com.example.demo.jvm.dynamicProxy;import com.example.demo.jvm.dynamicProxy.DynamicProxyTest.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;final class $Proxy0 extends Proxy implements IHello {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler var1) throws  {super(var1);}public final void sayHello() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}// 此处由于版面原因,省略equals()、hashCode()、toString()3个方法的代码
// 这3个方法的内容与sayHello()非常相似。static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m3 = Class.forName("com.example.demo.jvm.dynamicProxy.DynamicProxyTest$IHello").getMethod("sayHello");m2 = Class.forName("java.lang.Object").getMethod("toString");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

以上内容来自《深入理解Java虚拟机》:JVM高级特性与最佳实践(第三版) 周志明著,9.2.3 节 字节码生成技术与动态代理的实现

转载请在明显位置附上原文链接。

java详解动态代理中的代理对象相关推荐

  1. Java详解去除字符串中空格的方法

    Java去除字符串中空格的方法详解 代码中字符串使用了replaceAll()方法,去除了所有空格(其中包括:首尾空格.中间空格) 遂整理下java关于字符串去除空格的方法. 1.方法分类 str.t ...

  2. java调用项目中的文件_详解eclipse项目中.classpath文件的使用

    1 前言 在使用eclipse或者myeclipse进行java项目开发的时候,每个project(工程)下面都会有一个.classpath文件,那么这个文件究竟有什么作用? 2 作用 .classp ...

  3. java按钮权限控制_详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...

  4. idea看java版本设置_详解IntelliJ IDEA 中如何配置多个jdk版本即(1.7和1.8两个jdk都可用)...

    详解IntelliJ IDEA 中如何配置多个jdk版本即(1.7和1.8两个jdk都可用) 有时候需要看Java源码,但是 Java 1.7 和 Java 1.8的差别的关系,有时候你想查看不同jd ...

  5. 从零开始的Nginx详解(4)【Nginx-反向代理】

    Nginx-反向代理 一.代理简介 1.正常请求流程 2.正向代理 3.反向代理 二.反向代理配置 1.被代理服务器配置 2.代理服务器配置 3.测试 三.Nginx相关文章链接 演示环境: 系统版本 ...

  6. Mybatis的特性详解——动态SQL

    Mybatis的特性详解--动态SQL 前言 一.动态sql的元素 1.MyBatis if标签:条件判断 2.MyBatis choose.when和otherwise标签 3.MyBatis wh ...

  7. Java详解剑指offer面试题50--第一个只出现一次的字符

    Java详解剑指offer面试题50–第一个只出现一次的字符 找出字符串中找出第一个只出现一次的字符,比如输入"abacceff",则输出'b' 要想知道某个字符是不是只出现了一次 ...

  8. php图片涂鸦,IOS_详解iOS App中图片的线段涂鸦功能的添加方法,接下来我们要讲图片的涂鸦, - phpStudy...

    详解iOS App中图片的线段涂鸦功能的添加方法 接下来我们要讲图片的涂鸦,我们分开一点一点拓展,先给图片上划线 创建项目 起名testAddLine 接下来我们在默认生成的ViewControlle ...

  9. [深入浅出Cocoa]之消息(二)-详解动态方法决议(Dynamic Method Resolution)

    [深入浅出Cocoa]之消息(二)-详解动态方法决议(Dynamic Method Resolution) 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循&quo ...

最新文章

  1. 加权轮询算法PHP,PHP实现负载均衡的加权轮询方法分析
  2. @angular/compiler-cli@4.3.6 requires typescript@'=2.1.0 2.4.0' but 2.5.2 was found instead.
  3. Docker源码修改工作总结(三)
  4. Spring之Bean的配置(一)
  5. 【干货】美拍App是如何9个月做到用户过亿的
  6. TFS(Team Foundation Server)介绍和入门
  7. php+签到+二进制方式,PHP开发中如何实现二进制搜索?
  8. java 定时器框架_java定时器
  9. vim中实现javascript代码自动完成功能
  10. python end用法_8种高级的Python列表使用技巧,都给你整理好啦(附实操代码)
  11. 如何在 macOS Monterey 中使用预览合并 PDF?
  12. Linux sed命令之删除文件第一行,第n行
  13. 你专属的程序员春节“大礼包”
  14. Python压缩解压–zipfile
  15. 如何用深度学习对几种类型的图片进行分类(tensorflow,CNN)
  16. 用python的turtle画五角星
  17. Linux分卷压缩后,Windows解压缩
  18. 二级分销商城简单的设计方式
  19. java js hexmd5_JAVA与JS在MD5上问题
  20. 系统学习SSH(一)--SSH

热门文章

  1. 今天是我二十一岁的生日
  2. 【TWS API 使用教程7】如何使用TWS API 从盈透证券中筛选满足一定条件的contract?
  3. [CocoaPods]使用CocoaPods进行测试
  4. CPU核心数与线程数详解
  5. c# oldb连接_C#中Excel 2016的oledb连接字符串
  6. ctr预估 php,CTR预估与模型融合
  7. 数据结构之二叉树介绍
  8. html 网页黑夜模式,网站添加暗黑模式html+js
  9. Python情人节表白代码 画一箭穿心、小人儿发射爱心、520表白完整代码
  10. Istio-sidecar注入原理