动态代理在 Java 中有着广泛的应用,例如:Spring AOP 面向切面编程,Hibernate 数据查询、以及 RPC Dubbo 远程调用等,都有非常多的实际应用@mikechen
Java 动态代理原理
按照代理的创建时期,代理类可以分为两种:
- 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译,在程序运行前,代理类的 .class 文件就已经存在了。
- 动态代理:在程序运行时,可以运用反射机制动态创建代理类的 .class 文件。
动态代理类与静态代理类最主要的不同点是:代理类的字节码不是在程序运行前生成的,而是在程序运行时再虚拟机中程序自动创建的。
动态代理的实现方式很多。例如:JDK 自身提供的动态代理,就利用了上面提到的反射机制。除了反射,动态代理还可以通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。
简单来说,动态代理是一种行为方式,而 反射或 ASM 只是它的一种实现手段而已。
本文我主要详解 Java 动态代理的 2 种主流现方式:JDK 原生动态代理与 CGLib 。
JDK 原生动态代理
JDK Proxy 动态代理的实现无需引用第三方类,只需要实现 InvocationHandler 接口,重写 invoke() 方法即可,整个实现代码如下所示:、
以上程序的执行结果是:
动态代理之前的业务处理。
可以看出, JDK Proxy 实现动态代理的核心是实现 Invocation 接口,我们查看 Invocation 的源码,会发现里面其实只有一个 invoke() 方法,源码如下:
这是因为在动态代理中有一个重要的角色,也就是代理器,它用于统一管理被代理的对象,显然 InvocationHandler 就是这个代理器。而 invoke() 方法,则是触发代理的执行方法,我们通过实现 Invocation 接口来拥有动态代理的能力。
CGLib 动态代理实现
CGLIB (Code Generation Library) 是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改、和动态生成 CGLIB 通过继承方式实现代理。
在使用 CGLib 之前,我们要先在项目中引入 CGLib 框架,在 pom.xml 中添加如下配置:
CGLib 的实现代码:
以上程序的执行结果是:
方法调用前业务处理。
可以看出:
CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的。
唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类,来实现动态代理的。
因此,被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。
JDK 动态代理与 CGLib 的区别
1. JDK 动态代理具体实现原理
- 通过实现 InvocationHandler 接口,创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface ,来创建动态代理;
- 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入。
2. CGLib 动态代理
CGLib 是一个强大、高性能的 Code 生产类库,可以实现运行期动态扩展 java 类,Spring 在运行期间通过 CGlib 继承要被动态代理的类,重写父类的方法,实现 AOP 面向切面编程。
3. 两者对比
- JDK 动态代理是面向接口的。
- CGLib 动态代理是通过字节码底层继承要代理类来实现(如果被代理类被 final 关键字所修饰,会失败)。
4. 性能对比
- CGLib 所创建的动态代理对象,在实际运行时候的性能要比 JDK 动态代理高不少,有研究表明,大概要高出10倍;
- CGLib 在创建对象的时候所花费的时间,比 JDK 动态代理要多很多,有研究表明,大概要高出8倍。
因此,对于 singleton 的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,更适合采用 CGLib 动态代理,反之,则比较适用 JDK 动态代理。
以上,是关于 Java 动态代理原理、以及动态代理2 种实现方式的解析。
希望有所帮助,谢谢【关注+点赞+转发】支持。
作者简介
陈睿 | mikechen , 10年+大厂架构经验,「mikechen 的互联网架构」系列文章作者,专注于互联网架构技术。
????阅读「mikechen 的互联网架构」40W 字技术文章合集????
Java并发 | JVM | MySQL | Spring | Redis | 分布式 | 高并发
--- end ---