Java动态代理大揭秘:JDK代理 vs CGLIB代理详解
在Java开发中,动态代理是一项非常强大的技术,它让我们能够在不修改目标对象代码的情况下,动态地为其添加额外功能,比如日志打印、权限校验、事务管理等。你可能听说过两种主流的动态代理实现方式:JDK动态代理和CGLIB动态代理。它们各有特点,适用场景也不同。本文将带你深入了解这两种代理技术的原理、优缺点、代码示例以及在实际开发中的应用。
1. 动态代理简介
什么是代理模式?
代理模式(Proxy Pattern)是一种设计模式,它通过代理对象间接访问目标对象,能够在不改变目标对象代码的前提下,增强或控制目标对象的行为。
静态代理 vs 动态代理
- 静态代理:代理类的代码在编译时就写好,代码量大且不灵活。
- 动态代理:代理类在运行时动态生成,更灵活,适合实现AOP(面向切面编程)等需求。
动态代理极大地简化了代码编写,提高了代码复用性。
2. JDK动态代理
工作原理
JDK动态代理基于Java反射机制,通过java.lang.reflect.Proxy类和InvocationHandler接口实现动态代理。它只能代理实现了接口的类。
主要特点
- 只能代理实现了接口的类。
- 代理类在运行时动态生成。
- 性能较好。
- 使用简单,无需额外依赖。
代码示例
// Javaimport java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;// 目标接口interface HelloService { void sayHello();}// 目标对象实现接口class HelloServiceImpl implements HelloService { public void sayHello() { System.out.println(\"Hello, world!\"); }}// 动态代理处理器class HelloInvocationHandler implements InvocationHandler { private final Object target; public HelloInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(\"方法执行前...\"); Object result = method.invoke(target, args); System.out.println(\"方法执行后...\"); return result; }}public class JdkProxyDemo { public static void main(String[] args) { HelloService target = new HelloServiceImpl(); HelloService proxy = (HelloService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new HelloInvocationHandler(target) ); proxy.sayHello(); }}
运行结果
方法执行前...Hello, world!方法执行后...
3. CGLIB动态代理
原理
CGLIB(Code Generation Library)通过字节码生成技术,在运行时动态生成目标类的子类来实现代理。代理对象是目标类的一个子类,重写目标方法并插入增强逻辑。
特点
- 可以代理没有实现接口的类。
- 生成的代理类是目标类的子类,因此目标类不能是
final类。 - 代理方法不能是
final方法。 - 性能稍逊于JDK动态代理,但更灵活。
- 需要额外引入CGLIB库。
代码示例
// Javaimport net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 目标类class HelloService { public void sayHello() { System.out.println(\"Hello, CGLIB!\"); }}// CGLIB代理拦截器class HelloMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(\"方法执行前...\"); Object result = proxy.invokeSuper(obj, args); System.out.println(\"方法执行后...\"); return result; }}public class CglibProxyDemo { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloService.class); enhancer.setCallback(new HelloMethodInterceptor()); HelloService proxy = (HelloService) enhancer.create(); proxy.sayHello(); }}
运行结果
方法执行前...Hello, CGLIB!方法执行后...
4. JDK动态代理 vs CGLIB动态代理对比
5. Spring框架中的动态代理应用
- Spring默认使用JDK动态代理,如果目标类实现了接口。
- 如果目标类没有实现接口,Spring会自动切换到CGLIB代理。
- 可以通过配置强制使用CGLIB代理,例如在
@EnableAspectJAutoProxy(proxyTargetClass=true)中设置。
6. 其他相关知识点
动态代理的优点
- 减少重复代码,提高代码复用。
- 方便实现AOP(面向切面编程)。
- 灵活增强目标对象功能。
动态代理的缺点
- 代理类生成增加一定运行时开销。
- 可能增加调试难度,调用链复杂。
Java 8 Lambda与动态代理
Java 8引入了Lambda表达式,可以简化InvocationHandler的写法,更加简洁优雅。
其他字节码增强技术
除了CGLIB,还有ASM、Javassist等字节码操作库,能实现更底层的字节码增强。
总结
动态代理是Java开发中非常实用的技术,JDK动态代理和CGLIB动态代理各有优劣。选择哪种代理方式,取决于你的目标类是否实现了接口、性能需求和使用场景。理解它们的原理和特点,能帮助你更好地应用动态代理技术,写出高效、灵活的代码。最后,求一个大家的点赞和关注!谢谢大伙!


