JDK 动态代理和 CGLIB 代理是 Java 中两种常见的动态代理技术。它们的核心作用是 在运行时生成代理对象,用于增强原始对象的功能(如 AOP 切面编程、拦截方法调用等)。
① JDK 动态代理
JDK 动态代理基于 java.lang.reflect.Proxy
和 InvocationHandler
接口。它 仅能代理实现了接口的类 ,通过 Proxy.newProxyInstance
方法在运行时生成代理对象。
工作原理
- 代理类实现目标接口,并在
invoke
方法中拦截所有方法调用。 - 代理对象是
Proxy
类的子类,JVM 在运行时动态生成该类。 - 调用方法时,代理对象会调用
InvocationHandler
的invoke
方法。
示例
java
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 {
@Override
public 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;
}
@Override
public 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 依赖:
XML
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
java
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;
}
@Override
public 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。