静态代理、JDK和Cglib动态代理、回调
- [1. 静态代理:手工打造的"专属替身"](#1. 静态代理:手工打造的“专属替身”)
- [2. JDK动态代理:基于接口的"通用中介"](#2. JDK动态代理:基于接口的“通用中介”)
- [3. CGLIB代理:通过继承的"子类替身"](#3. CGLIB代理:通过继承的“子类替身”)
- [5. 总结](#5. 总结)
本篇后续推荐文章:事务拦截器TransactionInterceptor与@Transactional
1. 静态代理:手工打造的"专属替身"
-
核心概念 :在 编译期 ,由程序员 手动编写并创建 一个具体的代理类。
这个代理类实现了与目标对象相同的接口(或继承自目标类),并在内部持有对目标对象的引用。它通过在方法调用前后添加代码来增强功能。 -
关键特征 :
- 硬编码 :一个代理类只服务于一个(或一类)特定的目标类,是 "一对一" 或 "一对有限" 的静态关系。
- 提前存在 :代理类的
.java和.class文件在程序运行前就已存在。 - 直接控制:增强逻辑直接、明确地写在代理类的方法中。
-
设计哲学 :"定制化" 。像为某人量身定制一套西装,完全合身但制作成本高,且无法给另一个人穿。
-
类比 :明星的专属经纪人。经纪人(代理)为某位特定明星(目标)处理所有对外事务(增强),合同是长期固定的。
-
代码示例 :
java// 1. 定义接口(和动态代理一样) public interface PayService { void payment(); } // 2. 目标类(真实对象) public class AliPayServiceImpl implements PayService { @Override public void payment() { System.out.println("支付宝支付"); } } // 3. 静态代理类:手动编写,在编译前就已存在 public class PayStaticProxy implements PayService { // 持有一个目标对象的引用 private PayService payServiceImplTarget; PayStaticProxy(PayService payServiceImplTarget) { this.payServiceImplTarget = payServiceImplTarget; } @Override public void payment() { System.out.println("【静态代理】开始执行..."); // 增强逻辑(可以在目标对象的方法前和后做一些增强逻辑处理) long start = System.currentTimeMillis(); // 调用目标对象的方法 this.payServiceImplTarget.payment(); System.out.println("【静态代理】执行完毕..."); System.out.println("【静态代理】耗时:" + (end - start) + "ms"); } } // 4. 使用示例 public class StaticProxyDemo { public static void main(String[] args) { // 创建目标对象 PayService payService = new AliPayServiceImpl(); // 创建代理对象(手动new出来的!) PayStaticProxy payStaticProxy = new PayStaticProxy(payService); System.out.println("代理类名: " + payStaticProxy.getClass().getName()); // 输出: PayStaticProxy(一个实实在在的类) payStaticProxy.payment(); } }
2. JDK动态代理:基于接口的"通用中介"
-
核心概念 :在 运行时 ,通过Java原生的
java.lang.reflect.Proxy类,动态地在内存中生成 一个代理类。该代理类实现了目标对象的一组接口,并将所有方法调用 统一转发 到一个 调用处理器 (InvocationHandler)的invoke方法中。 -
关键特征 :
- 运行时生成 :代理类在程序运行时由JVM动态创建,其类名通常为
$Proxy0,$Proxy1等。默认生成的代理类 $Proxy0.class 在内存中,我们也可以保存JDK动态代理生成的.class文件。System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");,这个设置就可以把动态代理类保存到.class文件中。对应源码 :java.lang.reflect.ProxyGenerator#saveGeneratedFiles - 面向接口 :强制要求 目标对象必须至少实现一个接口。代理的是接口中定义的行为契约,而非具体类。
- 统一回调 :所有方法的增强逻辑都集中在
InvocationHandler.invoke()这一个方法中处理,是 "一对多" 的。
- 运行时生成 :代理类在程序运行时由JVM动态创建,其类名通常为
-
设计哲学 :"契约化" 。只要你遵守我的接口契约(合同),我就可以作为你的通用中介。核心是 "回调机制"。
-
类比 :标准化服务的呼叫中心 。用户(调用者)拨打标准号码(调用接口方法),呼叫中心(代理)根据来电类型,将请求路由给对应的处理专员(
InvocationHandler)按照标准流程(invoke方法)处理。 -
代码示例 :
java// 1. 定义接口(JDK代理必须要有接口) public interface PayService { void payment(); } // 2. 实现类 public class WxPayServiceImpl implements PayService { @Override public void payment() { System.out.println("微信支付"); } } // 3. 调用处理器(实现InvocationHandler) public class PayHandler implements InvocationHandler { // 目标对象 private Object target; public PayHandler(Object target) { this.target = target; } // 回调方法[的具体实现]:当代理对象的方法被调用时,会自动回调此方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【jdk动态代理】类执行开始..."); // 回调中的回调:调用原始目标方法 Object result = method.invoke(this.target, args); System.out.println("【jdk动态代理】类执行完毕..."); return result; } } // 4. 使用示例 public class JdkProxyDemo { public static void main(String[] args) { // 默认生成的代理类 $Proxy0.class 在内存中,实现了PayService接口。 // 当然,我们也可以保存JDK动态代理生成的.class文件 /** @see java.lang.reflect.ProxyGenerator#saveGeneratedFiles */ System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); // 1).创建目标对象 PayService payService = new WxPayServiceImpl(); // 2).创建回调处理器 InvocationHandler callback = new PayHandler(payService); // 3).创建代理对象(核心步骤) PayService proxy = (PayService) Proxy.newProxyInstance( payService.getClass().getClassLoader(), // 类加载器 payService.getClass().getInterfaces(), // 实现的接口 callback // 调用处理器 ); // 4).使用代理对象 System.out.println("代理类名: " + proxy.getClass().getName());// 输出: jdk.proxy1.$Proxy0 // 调用代理方法时,会自动触发 callback.invoke() proxy.payment(); } }
3. CGLIB代理:通过继承的"子类替身"
-
核心概念 :在 运行时 ,通过第三方库(如CGLIB)的
Enhancer类,动态地生成一个目标类的子类 作为代理。这个子类重写了目标类中所有 非final 的方法,并在其中插入拦截逻辑。 -
关键特征 :
- 运行时生成 :代理类也是在运行时生成,类名通常为
...$$EnhancerByCGLIB$$...。默认生成的代理类 ...$$EnhancerByCGLIB$$...class 在内存中,我们也可以保存CGLIB动态代理生成的.class文件。System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib");,这个设置就可以把动态代理类保存到.class文件中。对应源码 :org.springframework.cglib.core.DebuggingClassWriter#DEBUG_LOCATION_PROPERTY - 继承子类 :不需要目标对象实现接口 。它通过生成目标类的子类来实现代理,因此不能代理
final类或final方法。 - 方法拦截 :增强逻辑通过实现
MethodInterceptor接口,在intercept方法中定义,可以灵活地调用原始父类方法。
- 运行时生成 :代理类也是在运行时生成,类名通常为
-
设计哲学 :"继承化" 。通过成为目标的"儿子"来获得代表它的权利,并可以在继承的行为基础上进行修改。
-
类比 :公司的"太子"或接班人 。太子(代理)继承了父亲(目标)的所有职位和能力,但在处理某些公务时,会先咨询军师(
MethodInterceptor)的意见,再决定是否亲自执行或如何执行。 -
代码示例 :
java// 1. 目标类(不需要接口!) public class ProductService { public void saveProduct() { System.out.println("保存产品"); } // final方法不能被CGLIB代理 public final void audit() { System.out.println("审计日志"); } // private方法不会被代理 private void internal() { System.out.println("内部方法"); } } // 2. 方法拦截器(实现MethodInterceptor) public class CglibTransactionInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【CGLIB代理】开启事务 - 方法: " + method.getName()); // 调用父类方法 Object result = proxy.invokeSuper(obj, args); System.out.println("【CGLIB代理】提交事务"); return result; } } // 3. 使用示例 public class CglibProxyDemo { public static void main(String[] args) { // 1).设置CGLIB代理类输出目录 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib"); // 2).创建Enhancer(CGLIB的核心类) Enhancer enhancer = new Enhancer(); // 设置父类(被代理的类) enhancer.setSuperclass(ProductService.class); // 设置回调(拦截器) enhancer.setCallback(new CglibTransactionInterceptor()); // 3).创建代理对象 ProductService proxy = (ProductService) enhancer.create(); // 4).使用代理对象 System.out.println("代理类名: " + proxy.getClass().getName()); // 输出: ProductService$$EnhancerByCGLIB$$xxxxxx proxy.saveProduct(); System.out.println("---------------"); proxy.audit(); // final方法,不会被代理增强 } }
5. 总结
| 特性 | 静态代理 | JDK动态代理 | CGLIB代理 |
|---|---|---|---|
| 实现时期 | 编译期(.java文件已存在) | 运行期(程序自动生成) | 运行期(程序自动生成) |
| 实现方式 | 手动编写代理类 | Proxy类 + InvocationHandler接口 |
Enhancer类 + MethodInterceptor接口 |
| 与目标关系 | 实现相同接口或继承类 | 实现相同接口 | 继承目标类 |
| 目标要求 | 接口或类 | 必须实现接口 | 不能是final类 |
| 代理类来源 | 程序员手动编写 | Proxy.newProxyInstance() 生成 |
Enhancer.create() 生成 |
| 类文件 | 存在于源码中(.java → .class) | 运行时在内存中生成($Proxy0.class) |
运行时在内存中生成($$EnhancerBy...) |
| 性能 | 最好(直接调用) | 反射调用,较慢 | 通过FastClass机制,比JDK代理快 |
| 灵活性 | 低(每增一目标需写一代理) | 高(一个处理器可代理多类) | 高(一个拦截器可代理多类) |
| 本质 | 手工编写的"包装类" | 基于接口契约的动态回调 | 通过继承的动态子类 |
| 框架应用 | 简单封装、适配器模式 | Spring AOP(默认对接口) | Spring AOP(默认对类,或proxyTargetClass=true时) |
- 保存JDK动态代理类 :
- 设置方法 :
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); - 对应源码 :java.lang.reflect.ProxyGenerator#saveGeneratedFiles`
- 设置方法 :
- 保存CGLIB代理类 :
- 设置方法 :
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib"); - 对应源码 :org.springframework.cglib.core.DebuggingClassWriter#DEBUG_LOCATION_PROPERTY
- 设置方法 :
-
静态代理是最基础、最原始的代理模式实现。它与JDK动态代理、CGLIB代理的核心区别在于:静态代理是在编译期间手动创建的代理类,而动态代理是在运行时程序自动生成的代理类。
-
理解这三种代理的关键在于把握它们 创建代理对象的时机(编译时 vs 运行时) 和 与目标对象建立关系的方式(组合 vs 接口契约 vs 继承) 。动态代理(JDK/CGLIB)的强大之处在于将 "增强逻辑" 与 "代理对象创建" 解耦,通过 回调机制 实现了高度的灵活性和通用性。