动态代理与 AOP 原理
一、静态代理 vs 动态代理
┌─────────────────────────────────────────────────────────────────┐
│ 静态代理 vs 动态代理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 静态代理: │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Client │────▶│ ProxyUser │────▶│ RealUser │ │
│ │ │ │ Service │ │ Service │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ 问题:每个类都要写一个代理类 │
│ │
│ 动态代理: │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Client │────▶│ Proxy │────▶│ RealUser │ │
│ │ │ │ Generator │ │ Service │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ 运行时代理生成 │
│ │
└─────────────────────────────────────────────────────────────────┘二、JDK 动态代理
实现原理
java
public class JDKProxy implements InvocationHandler {
private Object target; // 被代理对象
public JDKProxy(Object target) {
this.target = target;
}
// 创建代理对象
public Object createProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口列表
this // InvocationHandler
);
}
@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;
}
}使用示例
java
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("save user");
}
}
// 创建代理
UserService proxy = (UserService) new JDKProxy(new UserServiceImpl()).createProxy();
proxy.save(user); // 通过代理调用三、CGLIB 字节码代理
实现原理
┌─────────────────────────────────────────────────────────────────┐
│ CGLIB 代理原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 被代理类: │
│ public class UserService { │
│ public void save() { } │
│ } │
│ │
│ CGLIB 动态生成的子类: │
│ public class UserService$$CGLIB extends UserService { │
│ private MethodInterceptor callback; │
│ │
│ @Override │
│ public void save() { │
│ callback.intercept(this, save method, null, null); │
│ } │
│ } │
│ │
│ intercept 方法中可以: │
│ - 前置增强 │
│ - 调用父类方法(被代理方法) │
│ - 后置增强 │
│ │
└─────────────────────────────────────────────────────────────────┘使用示例
java
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@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;
}
}四、JDK vs CGLIB
┌─────────────────────────────────────────────────────────────────┐
│ JDK 动态代理 vs CGLIB │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────┬──────────────────────────┐ │
│ │ JDK 动态代理 │ CGLIB │ │
│ ├──────────────────────────┼──────────────────────────┤ │
│ │ 基于接口 │ 基于继承 │ │
│ │ 被代理类必须实现接口 │ 被代理类可以无接口 │ │
│ │ Proxy.newProxyInstance │ Enhancer.create() │ │
│ │ 反射调用 │ 字节码生成,效率更高 │ │
│ │ InvocationHandler │ MethodInterceptor │ │
│ └──────────────────────────┴──────────────────────────┘ │
│ │
│ Spring 选择策略: │
│ - 目标类有接口 → JDK 动态代理 │
│ - 目标类无接口 → CGLIB │
│ - 强制使用 CGLIB → @EnableAspectJAutoProxy(proxyTargetClass=true)
│ │
└─────────────────────────────────────────────────────────────────┘五、Spring AOP 原理
代理创建时机
┌─────────────────────────────────────────────────────────────────┐
│ Spring AOP 代理创建流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Bean 实例化完成后 │
│ │ │
│ ▼ │
│ BeanPostProcessor.postProcessAfterInitialization() │
│ │ │
│ ├─ AnnotationAwareAspectJAutoProxyCreator │
│ │ │
│ ├── 1. 检查是否需要代理 │
│ │ - 有 @Aspect 切面? │
│ │ - 切点匹配当前 Bean? │
│ │ │
│ ├── 2. 选择代理方式 │
│ │ - 有接口 → JDK 动态代理 │
│ │ - 无接口 → CGLIB │
│ │ │
│ └── 3. 创建代理对象 │
│ 返回代理对象替换原始 Bean │
│ │
└─────────────────────────────────────────────────────────────────┘核心组件
1. AspectJExpressionPointcut → 切点表达式解析
2. AspectJAdvisorFactory → 从 @Aspect 类创建 Advisor
3. AnnotationAwareAspectJAutoProxyCreator → 创建 AOP 代理
4. JdkDynamicAopProxy / CglibAopProxy → 实际代理类六、事务代理原理
┌─────────────────────────────────────────────────────────────────┐
│ 声明式事务原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ @Transactional │
│ │ │
│ ▼ │
│ TransactionInterceptor(MethodInterceptor) │
│ │ │
│ ├── 1. 检查是否开启事务 │
│ │ │
│ ├── 2. 获取/创建事务 │
│ │ TransactionManager.getTransaction() │
│ │ │
│ ├── 3. 调用目标方法 │
│ │ │
│ ├── 4. 提交/回滚 │
│ │ commit() / rollback() │
│ │ │
│ └── 5. 异常处理 │
│ rollbackFor 配置的异常 → 回滚 │
│ │
└─────────────────────────────────────────────────────────────────┘七、面试高频问题
Q1: JDK 和 CGLIB 区别?
- JDK:基于接口,Proxy.newProxyInstance
- CGLIB:基于继承,Enhancer.create,效率更高
Q2: Spring 为什么默认用 JDK 代理?
- JDK 是标准 API,无需引入第三方库
- 如果目标类没有接口,Spring 才使用 CGLIB
Q3: AOP 代理调用流程?
- 客户端调用代理对象
- 代理对象调用 JdkDynamicAopProxy.invoke()
- 获取匹配的增强器链
- 依次执行各增强器的 before 方法
- 调用目标方法
- 依次执行各增强器的 after 方法
Q4: 代理对象方法调用目标对象?
通过 method.invoke(target, args) 调用
八、下一章预告
下一章我们将学习 事务传播行为:
- 7 种传播行为详解
- 嵌套事务与 savepoint
- 常见问题与解决方案