> 技术文档 > Java动态代理大揭秘:JDK代理 vs CGLIB代理详解

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动态代理对比

特性 JDK动态代理 CGLIB动态代理 代理方式 基于接口实现代理 基于继承生成子类代理 代理目标 只能代理实现了接口的类 可以代理普通类(非final类) 性能 较好 稍逊于JDK代理 依赖 JDK自带,无需额外依赖 需要引入CGLIB库 代理类限制 目标类必须实现接口 目标类不能是final,方法不能是final 使用场景 业务接口明确且有接口的情况 需要代理普通类,或接口不明确时

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动态代理各有优劣。选择哪种代理方式,取决于你的目标类是否实现了接口、性能需求和使用场景。理解它们的原理和特点,能帮助你更好地应用动态代理技术,写出高效、灵活的代码。最后,求一个大家的点赞和关注!谢谢大伙!