Java 动态代理
为什么需要代理
不希望或是不能直接访问某一对象 A,而是通过代理对象 B 间接访问。这种方式我们就称为代理。
这里对象 A 就称为委托类,也称为被代理类,对象 B 称为代理类。
代理有哪些优点?
- 隐藏委托类;
- 解耦。在不改变委托类的情况下做一些额外处理,比如添加初始判断及其他公共操作;
- AOP 编程;
静态代理
代理类 ProxySubject 中的方法,都指定地调用 RealSubject 中对应的方法。
示例:
1 | //定义委托行为 |
动态代理
1 | // package com.example.dynamicproxy |
动态代理工作的基本模式是对 Proxy 对象的方法调用都会转发到 InvocationHandler#invoke() 方法中,再通过反射调用委托对象的方法。
如下图所示:
动态代理有两个角色:
1. InvocationHandler 接口
1 | package java.lang.reflect; |
2. Proxy 类
1 | package java.lang.reflect; |
1 | package sun.misc; |
代理对象的创建步骤:
- 获取委托类的所有接口列表;
- 在
proxyClassCache
查找是否缓存有该代理对象; - 没有则调用
ProxyClassFactory#apply()
方法,生成代理对象; - 根据接口所在包,确定生成的代理类的类名,系统默认类名为
com.sun.proxy.$Proxy0
; - 通过
ProxyGenerator.generateProxyClass()
方法动态创建代理类的字节码文件; - 通过
Proxy#defineClass0()
本地方法将对应的字节码转换为对应的 Class 对象; - 以 InvocationHandler 为参数,反射生成代理对象;
- 当调用委托类方法的时候,都会调用 InvocationHandler#invoke() 方法,进而通过反射调用委托类的相关方法。
生成的代理类反编译后如下:
1 | public final class HelloImpl extends Proxy implements IHello { |
字节码框架
ASM
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为,分析类信息,甚至能够根据用户要求生成新类。
CGLIB
CGLIB(Code Generation Library) 是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理
,无法处理 final 的情况。
参考
[1] sun.misc.ProxyGenerator.java
[2] ASM is an all purpose Java bytecode manipulation and analysis framework
[3] AOP 的利器:ASM 3.0 介绍 - IBM
[4] cglib - Byte Code Generation Library is high level API to generate and transform Java byte code