JDK 动态代理和 CGLIB 代理是 Java 中两种常见的动态代理技术。它们的核心作用是 在运行时生成代理对象,用于增强原始对象的功能(如 AOP 切面编程、拦截方法调用等)。
① JDK 动态代理
JDK 动态代理基于 java.lang.reflect.Proxy
和 InvocationHandler
接口。它 仅能代理实现了接口的类,通过 Proxy.newProxyInstance
方法在运行时生成代理对象。
工作原理
- 代理类实现目标接口,并在
invoke
方法中拦截所有方法调用。 - 代理对象是
Proxy
类的子类,JVM 在运行时动态生成该类。 - 调用方法时,代理对象会调用
InvocationHandler
的invoke
方法。
示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 1. 定义接口
interface UserService {void save(String name);
}// 2. 目标对象
class UserServiceImpl implements UserService {@Overridepublic void save(String name) {System.out.println("保存用户: " + name);}
}// 3. 创建 InvocationHandler 处理器
class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("日志: 方法 " + method.getName() + " 被调用,参数:" + args[0]);Object result = method.invoke(target, args);System.out.println("日志: 方法 " + method.getName() + " 调用完成");return result;}
}// 4. 使用动态代理创建代理对象
public class JDKProxyDemo {public static void main(String[] args) {UserService target = new UserServiceImpl();UserService proxy = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new MyInvocationHandler(target));proxy.save("张三");}
}
输出
日志: 方法 save 被调用,参数:张三
保存用户: 张三
日志: 方法 save 调用完成
优缺点
✅ 优点:
- 依赖 JDK,直接使用,无需额外库。
- 线程安全,稳定性较高。
❌ 缺点:
- 只能代理实现了接口的类,不能代理普通类。
- 由于反射调用
invoke
,性能稍差(比 CGLIB 慢)。
② CGLIB 动态代理
CGLIB(Code Generation Library)是一个强大的 字节码生成技术,可以代理 没有实现接口的普通类,其底层依赖 ASM(Java 字节码操作库)。
工作原理
- CGLIB 生成 目标类的子类,重写其方法,并在方法调用时增强功能。
- 由于 CGLIB 直接操作字节码,比 JDK 代理更快(因为少了反射调用)。
- 但由于是继承方式,不能代理
final
方法。
示例
需要 引入 CGLIB 依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;// 1. 目标类(没有实现接口)
class UserService {public void save(String name) {System.out.println("保存用户: " + name);}
}// 2. CGLIB 代理类
class CglibProxy implements MethodInterceptor {private final Object target;public CglibProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("日志: 方法 " + method.getName() + " 被调用,参数:" + args[0]);Object result = proxy.invoke(target, args);System.out.println("日志: 方法 " + method.getName() + " 调用完成");return result;}// 生成代理对象public Object createProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}
}// 3. 使用 CGLIB 创建代理对象
public class CGLIBProxyDemo {public static void main(String[] args) {UserService target = new UserService();UserService proxy = (UserService) new CglibProxy(target).createProxy();proxy.save("李四");}
}
输出
日志: 方法 save 被调用,参数:李四
保存用户: 李四
日志: 方法 save 调用完成
优缺点
✅ 优点:
- 可以代理普通类(不要求接口)。
- 性能比 JDK 代理更好(不使用反射,而是直接生成字节码)。
❌ 缺点:
- 不能代理
final
类或final
方法(因为 CGLIB 通过继承来生成代理)。 - 额外依赖 CGLIB,需要额外的 Jar 包(不过 Spring 已内置 CGLIB)。
- 比 JDK 代理稍微复杂,但更灵活。
JDK 代理 vs CGLIB 代理 对比
特性 | JDK 动态代理 | CGLIB 代理 |
---|---|---|
代理方式 | 反射 InvocationHandler | 继承+ASM 字节码生成 |
是否需要接口 | 需要(只能代理接口) | 不需要(可代理普通类) |
代理对象类型 | Proxy 生成的匿名类 | 目标类的子类 |
是否可代理 final 方法 | 可以 | ❌ 不可以 |
性能 | 较慢(基于反射) | 较快(直接修改字节码) |
依赖 | JDK 自带 | 需额外依赖 CGLIB |
典型应用 | java.lang.reflect.Proxy 、Spring AOP(基于接口) | Spring AOP(无接口时) |
使用场景总结
- JDK 动态代理 适用于 已有接口 的情况,比如 Service 层的
UserService
、OrderService
。 - CGLIB 动态代理 适用于 无接口 的普通类,比如
UserService
直接写成普通类。
Spring AOP 使用情况
- 如果被代理的类实现了接口,Spring 默认使用 JDK 动态代理。
- 如果没有接口,Spring 使用 CGLIB 代理(
@EnableAspectJAutoProxy(proxyTargetClass = true)
强制 CGLIB)。
总结
- JDK 动态代理 是基于接口的反射机制,适用于 接口代理。
- CGLIB 代理 通过 字节码增强 来生成子类,适用于 普通类代理。
- Spring AOP 结合两者,默认用 JDK,必要时才用 CGLIB。
对于日常开发,如果类有接口,优先使用 JDK 动态代理;否则,使用 CGLIB。