02.1 — JDK 动态代理与 CGLIB
定位: Spring AOP 的两种代理实现方式——原理、对比与选择策略 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: 两种代理原理、Spring 选择策略、代码实现
一、这是什么?为什么需要它?
Spring AOP 需要在不修改目标类代码的情况下为方法增强。这通过代理模式实现——创建一个"代理对象"包裹目标对象,在代理对象中插入增强逻辑。
Spring 提供了两种代理方式:JDK 动态代理(基于接口)和 CGLIB(基于继承)。
二、原理拆解
2.1 对比
| 特性 | JDK 动态代理 | CGLIB 代理 |
|---|---|---|
| 原理 | 基于接口,生成实现类 | 基于继承,生成子类 |
| 目标要求 | 必须实现接口 | 无接口也可 |
| 速度 | 创建快,调用稍慢 | 创建慢(需生成字节码),调用快 |
| 限制 | 只能代理接口方法 | final 类/方法无法代理 |
| 依赖 | JDK 内置 | 需要 asm 字节码库 |
2.2 Spring 选择策略
Spring 决定代理方式
-> 目标 Bean 是否实现接口?
-> 是 -> 是否配置 proxyTargetClass=true?
-> 否 -> JDK 动态代理
-> 是 -> CGLIB 代理
-> 否 -> CGLIB 代理
-> 目标类是否 final?
-> 是 -> 抛出异常,无法代理Spring Boot 2.0+ 默认:spring.aop.proxy-target-class=true,优先使用 CGLIB。
2.3 代码示例
java
// JDK 动态代理
public class JdkProxyHandler implements InvocationHandler {
private final Object target;
public JdkProxyHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After: " + method.getName());
return result;
}
public static <T> T createProxy(T target, Class<T> interfaceType) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{interfaceType},
new JdkProxyHandler(target)
);
}
}
// CGLIB 代理
public class CglibProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After: " + method.getName());
return result;
}
public static <T> T createProxy(Class<T> targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new CglibProxyInterceptor());
return (T) enhancer.create();
}
}三、面试视角
| 追问 | 答案要点 |
|---|---|
| JDK 动态代理和 CGLIB 各自的实现原理? | JDK: 接口-based, Proxy.newProxyInstance;CGLIB: 继承-based, Enhancer+MethodProxy |
| Spring 如何选择代理方式? | 有接口用 JDK,无接口用 CGLIB;Spring Boot 默认用 CGLIB |
| CGLIB 能代理 final 类吗? | 不能,CGLIB 基于继承生成子类,final 类无法被继承 |
| JDK 动态代理为什么调用慢? | JDK 基于反射(Method.invoke),CGLIB 基于 FastClass 索引调用 |
📚 相关链接
- **AOP核心概念** — 代理的上层应用
- **@Transactional原理** — 声明式事务的代理基础
- **Bean生命周期** — AOP 代理在 postProcessAfterInitialization 生成
- ← 返回 **返回 AOP 索引**