JDK动态代理与CGLIB动态代理:原理、实现与对比
一、动态代理概述
在Java开发中,代理模式是一种常用的设计模式,它允许我们为其他对象提供一个代理以控制对这个对象的访问。动态代理则是在运行时动态创建代理类及其对象的技术,无需手动编写代理类代码。Java中主要有两种动态代理实现方式:JDK动态代理和CGLIB动态代理。
二、JDK动态代理
1. 基本原理
JDK动态代理是Java原生支持的代理技术,基于接口实现。它主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
核心特点:
- 只能代理实现了接口的类
- 通过反射机制实现
- JDK原生支持,无需额外依赖
2. 实现案例
假设我们有一个用户服务接口和实现类:
// 用户服务接口 public interface UserService { void addUser(String username); void deleteUser(String username); } // 用户服务实现 public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("添加用户: " + username); } @Override public void deleteUser(String username) { System.out.println("删除用户: " + username); } }创建代理处理器:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserServiceInvocationHandler implements InvocationHandler { private Object target; // 被代理的对象 public UserServiceInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【前置处理】方法 " + method.getName() + " 开始执行"); long start = System.currentTimeMillis(); // 执行目标方法 Object result = method.invoke(target, args); long end = System.currentTimeMillis(); System.out.println("【后置处理】方法 " + method.getName() + " 执行完成,耗时: " + (end - start) + "ms"); return result; } }创建代理对象:
import java.lang.reflect.Proxy; public class JdkProxyDemo { public static void main(String[] args) { // 创建目标对象 UserService userService = new UserServiceImpl(); // 创建代理处理器 UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService); // 创建代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler ); // 通过代理对象调用方法 proxy.addUser("张三"); proxy.deleteUser("李四"); } }输出结果:
【前置处理】方法 addUser 开始执行 添加用户: 张三 【后置处理】方法 addUser 执行完成,耗时: 1ms 【前置处理】方法 deleteUser 开始执行 删除用户: 李四 【后置处理】方法 deleteUser 执行完成,耗时: 0ms三、CGLIB动态代理
1. 基本原理
CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它可以在运行时扩展Java类和实现接口。与JDK动态代理不同,CGLIB通过继承目标类来实现代理,因此不需要目标类实现接口。
核心特点:
- 可以代理普通类,不要求实现接口
- 通过继承方式实现代理
- 需要引入CGLIB依赖
- 通过字节码技术生成子类
2. 实现案例
首先添加Maven依赖:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>假设我们有一个没有实现接口的商品服务类:
public class ProductService { public void addProduct(String productName) { System.out.println("添加商品: " + productName); } public void deleteProduct(String productName) { System.out.println("删除商品: " + productName); } }创建方法拦截器:
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class ProductServiceMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【CGLIB前置处理】方法 " + method.getName() + " 开始执行"); long start = System.currentTimeMillis(); // 调用父类(即目标类)的方法 Object result = proxy.invokeSuper(obj, args); long end = System.currentTimeMillis(); System.out.println("【CGLIB后置处理】方法 " + method.getName() + " 执行完成,耗时: " + (end - start) + "ms"); return result; } }创建代理对象:
import net.sf.cglib.proxy.Enhancer; public class CglibProxyDemo { public static void main(String[] args) { // 创建增强器 Enhancer enhancer = new Enhancer(); // 设置父类(目标类) enhancer.setSuperclass(ProductService.class); // 设置回调(拦截器) enhancer.setCallback(new ProductServiceMethodInterceptor()); // 创建代理对象 ProductService proxy = (ProductService) enhancer.create(); // 通过代理对象调用方法 proxy.addProduct("iPhone 13"); proxy.deleteProduct("MacBook Pro"); } }输出结果:
【CGLIB前置处理】方法 addProduct 开始执行 添加商品: iPhone 13 【CGLIB后置处理】方法 addProduct 执行完成,耗时: 1ms 【CGLIB前置处理】方法 deleteProduct 开始执行 删除商品: MacBook Pro 【CGLIB后置处理】方法 deleteProduct 执行完成,耗时: 0ms四、JDK动态代理与CGLIB动态代理的对比
特性
JDK动态代理
CGLIB动态代理
实现机制
基于接口,使用反射API
基于继承,使用字节码技术
目标类要求
必须实现至少一个接口
可以是普通类,不需要实现接口
性能
相对较慢
相对较快
依赖
JDK原生支持
需要引入CGLIB库
方法调用
通过反射调用
通过FastClass机制直接调用
代理类生成方式
运行时生成接口的实现类
运行时生成目标类的子类
final方法处理
无影响
无法代理final方法
创建速度
创建较快
创建较慢(需要生成字节码)
调用速度
调用较慢
调用较快
五、性能对比测试
让我们通过一个简单的性能测试来比较两种代理方式的差异:
public class ProxyPerformanceTest { public static void main(String[] args) { int count = 1000000; // JDK动态代理测试 UserService userService = new UserServiceImpl(); UserService jdkProxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserServiceInvocationHandler(userService) ); long jdkStart = System.currentTimeMillis(); for (int i = 0; i < count; i++) { jdkProxy.addUser("testUser"); } long jdkEnd = System.currentTimeMillis(); System.out.println("JDK动态代理耗时: " + (jdkEnd - jdkStart) + "ms"); // CGLIB动态代理测试 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ProductService.class); enhancer.setCallback(new ProductServiceMethodInterceptor()); ProductService cglibProxy = (ProductService) enhancer.create(); long cglibStart = System.currentTimeMillis(); for (int i = 0; i < count; i++) { cglibProxy.addProduct("testProduct"); } long cglibEnd = System.currentTimeMillis(); System.out.println("CGLIB动态代理耗时: " + (cglibEnd - cglibStart) + "ms"); } }典型测试结果(100万次调用):
JDK动态代理耗时: 342ms CGLIB动态代理耗时: 198ms六、使用场景建议
- 目标对象已经实现了接口
- 系统对性能要求不是特别高
- 希望减少外部依赖
- Spring AOP默认对接口实现使用JDK动态代理
- 目标对象没有实现任何接口
- 需要代理普通类
- 对性能有较高要求
- 需要代理的方法不是final方法
- Spring AOP默认对非接口实现使用CGLIB
七、Spring框架中的选择
在Spring框架中,动态代理的选择遵循以下规则:
八、总结
JDK动态代理和CGLIB动态代理各有优缺点,在实际开发中应根据具体需求选择合适的代理方式。理解它们的实现原理和差异,有助于我们在设计系统时做出更合理的选择,也能在面试中更好地展示我们的技术深度。
对于大多数现代应用,性能差异已经不再是决定性因素,代码的可维护性和清晰度往往更为重要。Spring等框架已经为我们做好了默认选择,但在需要定制化代理行为或解决特定问题时,深入理解这些技术就显得尤为重要了。
谁与争锋,JDK动态代理大战CGLib动态代理jdk动态代理和cglib动态代理
代理模式-动态代理详解(JDK动态代理和CGLIB动态代理)jdk cglib动态代理
静态代理与JDK动态代理与CGLIB动态代理cglib动态代理
JDK动态代理和CGLib动态代理的对比cglib代理和jdk动态代理区别
jdk动态代理和cglib动态代理的原理分析cglib代理和jdk动态代理区别
【Java基础】——JDK动态代理VSCGLIB动态代理java jdk动态代理原理
代理模式之静态代理,JDK动态代理和cglib动态代理jdk cglib动态代理
java中的静态代理、jdk动态代理和cglib动态代理jdk cglib动态代理