一、AOP 核心概念

1.1 什么是 AOP?

1.2 核心术语

mindmap
  root((AOP 核心术语))
    切面 Aspect
      切点 Pointcut
      通知 Advice
    连接点 JoinPoint
      方法执行
      字段访问
    目标对象 Target
      被代理的对象
    代理对象 Proxy
      增强后的对象
    织入 Weaving
      编译期
      类加载期
      运行期
术语英文说明示例
切面Aspect横切关注点的模块化@Aspect 注解的类
连接点JoinPoint程序执行的某个位置方法执行、异常抛出
切点Pointcut匹配连接点的表达式@Pointcut("execution(_ com.._.*(..))")
通知Advice切面在特定连接点执行的动作@Before、@After、@Around
目标对象Target被代理的对象UserService
代理对象Proxy增强后的对象$Proxy、UserService$$EnhancerBySpringCGLIB
织入Weaving将切面应用到目标对象的过程Spring AOP 在运行时织入

1.3 通知类型(5 种)

@Aspect
@Component
public class LoggingAspect {

    // 1. 前置通知:方法执行前
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("前置通知:" + joinPoint.getSignature().getName());
    }

    // 2. 后置通知:方法执行后(无论成功或异常)
    @After("execution(* com.example.service.*.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("后置通知:" + joinPoint.getSignature().getName());
    }

    // 3. 返回通知:方法成功返回后
    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))",
                     returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("返回通知:" + joinPoint.getSignature().getName() + ", 结果:" + result);
    }

    // 4. 异常通知:方法抛出异常后
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
                   throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        System.out.println("异常通知:" + joinPoint.getSignature().getName() + ", 异常:" + ex.getMessage());
    }

    // 5. 环绕通知:包围方法执行(最强大)
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知 - 前");
        Object result = joinPoint.proceed();  // 执行目标方法
        System.out.println("环绕通知 - 后");
        return result;
    }
}
通知类型注解执行时机能否修改参数能否修改返回值能否阻止执行
前置通知@Before方法执行前
后置通知@After方法执行后
返回通知@AfterReturning方法成功返回后
异常通知@AfterThrowing方法抛出异常后
环绕通知@Around包围方法

二、JDK 动态代理

2.1 核心原理

2.2 使用示例

// 1. 定义接口
public interface UserService {
    void save();
    void delete();
}

// 2. 目标类(实现接口)
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }
}

// 3. JDK 动态代理实现
public class JDKProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),           // 类加载器
            target.getClass().getInterfaces(),            // 目标类实现的接口
            new InvocationHandler() {                     // 调用处理器
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("前置增强");
                    Object result = method.invoke(target, args);  // 调用目标方法
                    System.out.println("后置增强");
                    return result;
                }
            }
        );

        proxy.save();  // 调用代理对象的方法
    }
}

输出

前置增强
保存用户
后置增强

2.3 JDK 动态代理源码分析

// java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    // 1. 检查参数
    Objects.requireNonNull(h);

    // 2. 获取代理类(可能从缓存获取)
    Class<?> cl = getProxyClass0(loader, intfs);

    // 3. 获取代理类的构造器(参数为 InvocationHandler)
    final Constructor<?> cons = cl.getConstructor(constructorParams);

    // 4. 创建代理对象实例
    return cons.newInstance(new Object[]{h});
}

// 生成的代理类(简化版)
public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.UserService").getMethod("save");
        } catch (NoSuchMethodException var2) { }
    }

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    public final void save() {
        try {
            super.h.invoke(this, m3, null);  // 调用 InvocationHandler
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

关键点

  1. 代理类 $Proxy0 继承 Proxy,实现 UserService 接口
  2. 方法调用被转发到 InvocationHandler#invoke()
  3. 通过反射调用目标方法 method.invoke(target, args)

2.4 JDK 动态代理执行流程

sequenceDiagram
    participant Client
    participant Proxy as 代理对象($Proxy0)
    participant IH as InvocationHandler
    participant Target as 目标对象

    Client->>Proxy: proxy.save()
    Proxy->>IH: invoke(proxy, method, args)
    IH->>IH: 前置增强
    IH->>Target: method.invoke(target, args)
    Target-->>IH: 返回结果
    IH->>IH: 后置增强
    IH-->>Proxy: 返回结果
    Proxy-->>Client: 返回结果

三、CGLIB 动态代理

3.1 核心原理

3.2 使用示例

// 1. 目标类(无需实现接口)
@Service
public class OrderService {
    public void create() {
        System.out.println("创建订单");
    }

    public void cancel() {
        System.out.println("取消订单");
    }
}

// 2. CGLIB 代理实现
public class CGLIBProxyTest {
    public static void main(String[] args) {
        OrderService target = new OrderService();

        // 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());          // 设置父类
        enhancer.setCallback(new MethodInterceptor() {      // 设置回调
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("前置增强");
                Object result = proxy.invokeSuper(obj, args);  // 调用父类方法
                System.out.println("后置增强");
                return result;
            }
        });

        OrderService proxy = (OrderService) enhancer.create();
        proxy.create();  // 调用代理对象的方法
    }
}

输出

前置增强
创建订单
后置增强

3.3 CGLIB 工作原理

flowchart TD
    A[Enhancer 创建代理] --> B[生成目标类的子类]
    B --> C[重写父类的非 final 方法]
    C --> D[设置 MethodInterceptor 回调]
    D --> E[调用代理方法]

    E --> F{方法是否被拦截?}
    F -->|是| G[执行 MethodInterceptor.intercept]
    F -->|否| H[直接调用父类方法]

    G --> I[前置增强]
    I --> J[proxy.invokeSuper 调用父类]
    J --> K[后置增强]
    K --> L[返回结果]

3.4 JDK vs CGLIB 代理类对比

特性JDK 动态代理CGLIB 动态代理
代理类名$Proxy0$Proxy1OrderService$$EnhancerBySpringCGLIB$$12345
继承关系继承 Proxy,实现接口继承目标类
方法调用InvocationHandler#invoke()MethodInterceptor#intercept()
目标方法调用method.invoke(target, args)proxy.invokeSuper(obj, args)
类加载器目标类类加载器自定义 ClassLoader

四、JDK vs CGLIB 对比

4.1 核心区别

flowchart LR
    A[目标类是否实现接口] -->|是| B{强制使用 CGLIB?}
    A -->|否| C[使用 CGLIB]

    B -->|是| C
    B -->|否| D[使用 JDK 动态代理]
对比维度JDK 动态代理CGLIB 动态代理
实现原理反射机制字节码生成(ASM)
代理类继承继承 Proxy,实现接口继承目标类
JDK 支持原生支持需要引入 CGLIB 依赖
final 类、方法不影响 无法代理
Spring 优先选择 优先选择降级方案
性能创建快,执行稍慢创建慢,执行快

4.2 Spring AOP 代理选择策略

// DefaultAopProxyFactory#createProxy()
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 如果配置了强制使用 CGLIB或者目标对象没有实现任何接口
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        // 使用 CGLIB 动态代理
        return new CglibAopProxy(config);
    } else {
        // 其他情况都使用 JDK 动态代理
        return new JdkDynamicAopProxy(config);
    }
}

选择策略

  1. 目标类实现接口 → 使用 JDK 动态代理
  2. 目标类未实现接口 → 使用 CGLIB
  3. 强制使用 CGLIB@EnableAspectJAutoProxy(proxyTargetClass = true)

五、Spring AOP 实现机制

5.1 Spring AOP 核心组件

flowchart TD
    A["@Aspect 注解类"] --> B[AspectJParser]
    B --> C[Advisor 通知器]

    C --> D[PointcutAdvisor]
    C --> E[IntroductionAdvisor]

    D --> F[Pointcut 切点]
    D --> G[Advice 通知]

    F --> H[Pointcut Expression]
    G --> I[MethodBeforeAdvice]
    G --> J[AfterReturningAdvice]
    G --> K[AfterThrowingAdvice]
    G --> L[AroundAdvice]
组件说明示例
Advisor通知器,包含 Pointcut 和 AdviceAspectJExpressionPointcutAdvisor
Pointcut切点,匹配连接点execution(* com.example.service.*.*(..))
Advice通知,在切点执行的动作MethodBeforeAdviceAroundAdvice
JoinPoint连接点,程序执行的位置方法执行、异常抛出

5.2 Spring AOP 代理创建流程

// AbstractAutoProxyCreator#createProxy()
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                             @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    // 1. 获取 Advisors(通知器)
    Object[] advisors = buildAdvisors(beanName, specificInterceptors);

    // 2. 创建代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this.advised);

    // 3. 判断使用 JDK 还是 CGLIB
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldUseCGLIB(proxyFactory)) {  // 判断是否使用 CGLIB
            proxyFactory.setProxyTargetClass(true);
        }
    }

    // 4. 创建代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

5.3 Spring AOP 代理执行流程

sequenceDiagram
    participant Client
    participant Proxy as 代理对象
    participant AdvisorChain as AdvisorChain
    participant Advisor1 as Advisor1
    participant Advisor2 as Advisor2
    participant Target as 目标对象

    Client->>Proxy: proxy.method()
    Proxy->>AdvisorChain: 获取 AdvisorChain

    loop 每个 Advisor
        AdvisorChain->>Advisor1: 执行前置通知
        AdvisorChain->>Advisor2: 执行前置通知
    end

    AdvisorChain->>Target: 调用目标方法
    Target-->>AdvisorChain: 返回结果

    loop 每个 Advisor(倒序)
        AdvisorChain->>Advisor2: 执行后置通知
        AdvisorChain->>Advisor1: 执行后置通知
    end

    AdvisorChain-->>Proxy: 返回结果
    Proxy-->>Client: 返回结果

六、@Aspect 注解原理

6.1 @Aspect 注解解析

@Aspect
@Component
public class TransactionAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("前置通知");
    }

    @Around("serviceLayer()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知 - 前");
        Object result = joinPoint.proceed();
        System.out.println("环绕通知 - 后");
        return result;
    }
}

6.2 @Aspect 注解解析流程

// AspectJAutoProxyBeanPostProcessor#postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
    // 1. 扫描 @Aspect 注解的类
    if (bean.getClass().isAnnotationPresent(Aspect.class)) {
        // 2. 解析 @Pointcut、@Before、@After 等注解
        parseAspectAnnotations(bean);
    }
    return bean;
}

private void parseAspectAnnotations(Object aspectBean) {
    Class<?> aspectClass = aspectBean.getClass();

    // 1. 解析 @Pointcut
    for (Method method : aspectClass.getDeclaredMethods()) {
        if (method.isAnnotationPresent(Pointcut.class)) {
            Pointcut pointcut = method.getAnnotation(Pointcut.class);
            // 保存切点表达式
            registerPointcut(pointcut.value(), method);
        }
    }

    // 2. 解析 @Before、@After、@Around
    for (Method method : aspectClass.getDeclaredMethods()) {
        if (method.isAnnotationPresent(Before.class)) {
            Before before = method.getAnnotation(Before.class);
            // 创建 Advisor:Pointcut + Advice
            Advisor advisor = createAdvisor(before.value(), method, aspectBean);
            registerAdvisor(advisor);
        }
    }
}

6.3 @Aspect 注解到 Advisor 的映射

@Aspect 注解Advice 类型Advisor 类型执行时机
@BeforeMethodBeforeAdviceAspectJMethodBeforeAdvice方法执行前
@AfterAfterAdviceAspectJAfterAdvice方法执行后(finally)
@AfterReturningAfterReturningAdviceAspectJAfterReturningAdvice方法成功返回后
@AfterThrowingThrowsAdviceAspectJAfterThrowingAdvice方法抛出异常后
@AroundMethodInterceptorAspectJAroundAdvice包围方法执行

七、代理失效场景

7.1 自调用导致代理失效

问题代码

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void methodA() {
        methodB();  //  自调用,@Transactional 失效
    }

    @Transactional
    public void methodB() {
        userRepository.save();
    }
}

原因分析

flowchart TD
    A[外部调用 userService.methodA] --> B[代理对象拦截]
    B --> C[执行 @Transactional 前置逻辑]
    C --> D[调用目标对象 methodA]
    D --> E[methodA 内部调用 methodB]
    E --> F[直接调用 this.methodB]
    F --> G[" 绕过代理,@Transactional 失效"]

解决方案

// 方案1:自己注入自己(推荐)⭐⭐⭐⭐⭐
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserService self;  // Spring 会注入代理对象

    @Transactional
    public void methodA() {
        self.methodB();  //  通过代理对象调用
    }

    @Transactional
    public void methodB() {
        userRepository.save();
    }
}

// 方案2:使用 AopContext.currentProxy()
@Service
public class UserService {
    @Transactional
    public void methodA() {
        ((UserService) AopContext.currentProxy()).methodB();  //  通过代理对象调用
    }
}

// 方案3:拆分到另一个 Service
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserLogService userLogService;

    @Transactional
    public void methodA() {
        userLogService.log();  //  调用其他 Service 的代理对象
    }
}

@Service
public class UserLogService {
    @Transactional
    public void log() {
        // 日志逻辑
    }
}

// 方案4:编程式事务

7.2 private/方法导致代理失效

问题代码

@Service
public class UserService {
    @Transactional
    private void methodB() {  //  private 方法无法代理
        userRepository.save();
    }
}

原因:private 方法无法被代理类重写(JDK)或继承(CGLIB)

解决方案:将方法改为 public 或 protected

7.3 final 方法导致代理失效

问题代码

@Service
public class UserService {
    @Transactional
    public final void methodB() {  //  final 方法无法被 CGLIB 重写
        userRepository.save();
    }
}

原因:final 方法无法被 CGLIB 子类重写

解决方案:去掉 final 修饰符


八、自检

Q1: JDK 动态代理和 CGLIB 的区别?⭐⭐⭐⭐⭐


Q2: Spring AOP 什么时候使用 JDK,什么时候使用 CGLIB?⭐⭐⭐⭐⭐


Q3: 为什么 @Transactional 在自调用时失效?⭐⭐⭐⭐⭐


Q4: @Around 和 @Before 的区别?⭐⭐⭐⭐


Q5: Spring AOP 和 AspectJ 的区别?⭐⭐⭐⭐


Q6: 如何理解 JoinPoint 和 ProceedingJoinPoint?⭐⭐⭐⭐


Q7: Spring AOP 的实现原理?⭐⭐⭐⭐⭐


核心源码路径

// JDK 动态代理
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler

// CGLIB 动态代理
org.springframework.cglib.proxy.Enhancer
org.springframework.cglib.proxy.MethodInterceptor
org.springframework.cglib.proxy.MethodProxy

// Spring AOP 核心类
org.springframework.aop.framework.ProxyFactory
org.springframework.aop.framework.JdkDynamicAopProxy
org.springframework.aop.framework.CglibAopProxy
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
org.springframework.aop.framework.DefaultAopProxyFactory

// @Aspect 注解解析
org.springframework.aop.aspectj.annotation.AspectJProxyFactory
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com