JDK 动态代理和 CGLIB 动态代理
JDK 动态代理和 CGLIB 动态代理是 Spring AOP 以及许多 Java 框架中常见的两种动态代理方式,它们在实现方式、使用场景和性能上有所不同。
1. JDK 动态代理
特点
- 依赖
java.lang.reflect.Proxy
和InvocationHandler
进行代理 - 只能代理接口,不能直接代理具体类
- 基于 Java 反射,在运行时生成代理类
实现方式
- 目标类必须实现 接口
- 使用
Proxy.newProxyInstance()
生成代理对象 - 代理方法调用时,委托给
InvocationHandler
处理
示例
import java.lang.reflect.*;
interface Service {
void doSomething();
}
class RealService implements Service {
public void doSomething() {
System.out.println("真实业务逻辑执行");
}
}
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("JDK 代理 - 方法执行前");
Object result = method.invoke(target, args);
System.out.println("JDK 代理 - 方法执行后");
return result;
}
}
public class JDKProxyTest {
public static void main(String[] args) {
Service target = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler(target)
);
proxy.doSomething();
}
}
运行结果
JDK 代理 - 方法执行前
真实业务逻辑执行
JDK 代理 - 方法执行后
优缺点
✅ 优点:
- 基于 JDK 反射,不依赖第三方库,性能较稳定
- 避免类加载问题(无需操作字节码)
- 支持多个接口的代理
❌ 缺点:
- 只能代理接口,不能代理普通类
- 性能比 CGLIB 稍低(JDK 代理基于反射,CGLIB 直接使用字节码增强)
2. CGLIB 动态代理
特点
- 依赖 ASM 字节码技术,通过继承目标类创建子类代理
- 可以代理普通类(不需要实现接口)
- 基于子类代理,无法代理
final
类或final
方法
实现方式
- 目标类不需要接口
- CGLIB 生成子类代理对象
- 拦截方法调用并增强逻辑
示例
import net.sf.cglib.proxy.*;
class RealService {
public void doSomething() {
System.out.println("真实业务逻辑执行");
}
}
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB 代理 - 方法执行前");
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB 代理 - 方法执行后");
return result;
}
}
public class CGLIBProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
enhancer.setCallback(new MyMethodInterceptor());
RealService proxy = (RealService) enhancer.create();
proxy.doSomething();
}
}
运行结果
CGLIB 代理 - 方法执行前
真实业务逻辑执行
CGLIB 代理 - 方法执行后
优缺点
✅ 优点:
- 可以代理普通类,不要求实现接口
- 性能比 JDK 代理更高(直接操作字节码,无需反射)
❌ 缺点:
- 无法代理
final
类和final
方法 - 生成代理类耗时更长(类字节码需要动态修改)
- 需要额外依赖 CGLIB(Spring 3.2 以前),Spring 4+ 自带
ByteBuddy
作为替代
3. JDK 动态代理 vs CGLIB
对比项 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
实现方式 | 反射(Proxy + InvocationHandler ) | 继承(字节码增强 Enhancer ) |
是否需要接口 | 需要(只能代理接口) | 不需要(可代理普通类) |
性能 | 反射调用,性能略低 | 直接调用字节码,性能更高 |
是否支持 final 类 | 支持 | 不支持 |
Spring AOP 代理方式 | 默认使用(若有接口) | 无接口时使用 |
应用场景 | 代理接口服务(如 RPC、Spring AOP) | 需要代理普通类(如 MyBatis) |
4. Spring AOP 选择哪种代理?
Spring AOP 默认选择 JDK 动态代理,除非目标类没有实现接口,才会使用 CGLIB:
- 有接口 → JDK 代理(默认)
- 无接口 → CGLIB 代理
如果希望 Spring 强制使用 CGLIB,可以:
@EnableAspectJAutoProxy(proxyTargetClass = true)
5. 总结
- JDK 动态代理:基于反射,只能代理接口,适用于接口驱动的场景。
- CGLIB 动态代理:基于字节码增强,可以代理普通类,性能更优,但不能代理
final
类和final
方法。 - Spring AOP:默认优先使用 JDK 代理,只有当目标类没有实现接口时才使用 CGLIB。
在实际开发中:
- 如果业务主要是接口编程(如 Service 层),JDK 代理更合适
- 如果需要代理普通类(如工具类),CGLIB 是更好的选择
两者各有优势,选择合适的方式能提升代理的效率和可维护性。