Java核心 -- 动态代理
编程语言分类
- 动态类型和静态类型:语言类型是运行时检查,还是编译期检查
- 强类型和弱类型:为不同类型的变量赋值时,是否需要进行显式的类型转换
- Java是静态的强类型语言,但提供了类似反射等机制,因此也具备了部分动态类型语言的能力
反射
- 反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省的能力
- 通过反射可以直接操作类或者对象
- 获取某个对象的类定义
- 获取类声明的属性和方法
- 调用方法或者构造函数
- 运行时修改类定义
setAccessible
- AccessibleObject.setAccessible(boolean flag):可以在运行时修改成员的访问限制
- setAccessible的应用遍布在日常开发、测试、依赖注入等框架中
- 在O/R Mapping框架中,为一个Java实体对象,运行时自动生成getter/setter方法
- 绕过API的访问控制,来调用内部API
动态代理
- 动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制
- 很多场景都是利用类似的机制来实现的,例如用来包装RPC调用和AOP
- 实现动态代理的方式
- JDK自身提供的动态代理,主要利用反射机制
- 字节码操作机制,类似ASM、cglib(基于ASM)和Javassist
解决的问题
- 动态代理是一种代理机制,代理可以看作对调用目标的包装,对目标代码的调用是通过代理完成的
- 通过代理可以让调用者和实现者解耦,例如RPC调用,框架内部的寻址、序列化、反序列化等,对调用者没什么意义
发展历程
- 静态代理 -> 动态代理
- 静态代理:需要引入额外的工作,而这些工作与实际的业务逻辑没有关系
- 古董技术RMI,需要rmic之类的工具生成静态stub等文件,增加了很多繁琐的准备工作
- 动态代理:相应的stub等类,可以在运行时生成,对应的调用操作也是动态生成的,极大地提高生产力
JDK Proxy + cglib
1 | public class MyDynamicProxy { |
- 实现InvocationHandler,添加额外逻辑
- 以Hello接口为纽带,为被调用目标构建代理对象,可以使用代理对象间接运行调用目标的逻辑
- 以接口为中心,相当于添加了一种对被调用者没有太大意义的限制
- 另外实例化的是Proxy对象,而不是真正的被调用类型,可能会带来各种不变和能力退化
- 如果被调用者没有实现接口,可以通过cglib来实现动态代理(克服了对接口的依赖)
- cglib动态代理的方式:创建目标类的子类,可以达到近似使用被调用者本身的效果
优势对比
JDK Proxy
- 最小化依赖关系,简化开发和维护,JDK本身的支持
- JDK平滑升级,而字节码类库通常需要进行更新以保证在新版Java上能够使用
- 代码实现简单
cglib
- 侵入性更小,JDK Proxy是基于接口的,而限定被调用者实现特定接口是有侵入性的实践
- 只需操作关心的类,而不必为其它相关类增加工作量
- 高性能
性能对比
- 在主流的JDK版本中,JDK Proxy在典型场景可以提供对等的性能水平,在数量级的差距并不是广泛存在的
- 反射机制的性能在现代JDK中,已经得到了极大的改进和优化,同时JDK的很多功能同样使用了ASM进行字节码操作
- 在选型时,性能并不是唯一考量,而可靠性、可维护性和编程工作量才是更主要的考虑因素
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.