Spring AOP原理(三):源码剖析代理创建时机,AOP失效场景全解!

系列文章第3篇(完结篇)| 共3篇
难度:⭐⭐⭐⭐ | 适合人群:想彻底搞懂Spring AOP的开发者


📝 系列回顾

经过前两篇的学习,我们已经掌握了:

  • 第一篇: JDK动态代理和CGLIB动态代理原理
  • 第二篇: @Aspect注解、5种通知类型、切点表达式

上期思考题解答:

Q1: 多个切面如何控制执行顺序?
A: 使用@Order注解或实现Ordered接口,数字越小越优先。(本篇详解)

Q2: @Around能完全替代其他4种通知吗?
A: 理论上可以,但不推荐。其他通知语义更清晰,代码更简洁。

Q3: 代理对象什么时候创建的?
A: 今天详细解答!


💥 开场:一次"见鬼"的AOP失效

时间: 周三晚上9点
地点: 家里(接到紧急电话)
事件: 线上日志丢失

运维: "今天下午的操作日志全没了,你的日志切面是不是有问题?"

我: "不可能啊,我测试过的..." 😰


紧急排查,发现代码:

@Service
public class UserService {
    
    @Autowired
    private UserDao userDao;
    
    @Log("创建用户")  // 有日志注解
    public void createUser(User user) {
        userDao.save(user);
    }
    
    @Log("批量创建")  // 有日志注解
    public void batchCreate(List<User> users) {
        for (User user : users) {
            this.createUser(user);  // ← 问题在这里!
        }
    }
}

测试:

// 直接调用createUser - 日志正常
userService.createUser(user);  // ✅ 日志记录

// 通过batchCreate调用 - 日志丢失
userService.batchCreate(users);  // ❌ 日志丢失

我: "又是this调用的问题?" 😱

哈吉米(电话里): "对,this调用拿到的是原始对象,不是代理对象。AOP是通过代理实现的,绕过代理当然不生效!"

我: "那代理对象到底是怎么创建的?什么时候创建的?为什么this不是代理?" 🤔

南北绿豆(也在线): "这就要深入源码了,我给你讲讲..."


🔍 第一问:代理对象的创建时机

Bean生命周期中的位置

阿西噶阿西: "还记得Bean的11个生命周期步骤吗?"

1. 实例化Bean
2. 属性填充
3. BeanNameAware
4. BeanFactoryAware
5. ApplicationContextAware
6. BeanPostProcessor - Before
7. @PostConstruct
8. InitializingBean
9. init-method
10. BeanPostProcessor - After  ← AOP代理在这里创建!
11. 销毁

代理创建在第10步:BeanPostProcessor的后置处理!


AbstractAutoProxyCreator源码

位置: org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    /**
     * BeanPostProcessor后置处理方法
     * 这里创建AOP代理
     */
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            
            // 如果不是提前暴露的Bean
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 如果需要代理,创建代理对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    /**
     * 如果需要,包装成代理对象
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        
        // 1. 已经处理过了,直接返回
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        
        // 2. 不需要增强,直接返回
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        
        // 3. 是基础设施类或应该跳过,直接返回
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
        
        // 4. 获取所有适用于这个Bean的增强器(Advisor)
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
            bean.getClass(), beanName, null);
        
        // 5. 如果有增强器,创建代理
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            
            // 创建代理对象
            Object proxy = createProxy(
                bean.getClass(), 
                beanName, 
                specificInterceptors, 
                new SingletonTargetSource(bean));
            
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;  // 返回代理对象
        }
        
        // 6. 没有增强器,返回原始Bean
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}

创建代理的核心方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
    // 1. 创建ProxyFactory
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    
    // 2. 决定使用JDK还是CGLIB
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);  // 使用CGLIB
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);  // 使用JDK
        }
    }
    
    // 3. 构建增强器数组
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    
    // 4. 定制ProxyFactory
    customizeProxyFactory(proxyFactory);
    
    // 5. 创建代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

流程图

Bean创建流程
│
├─ 1-9步:实例化、属性填充、初始化
│
├─ 10. BeanPostProcessor后置处理
│    └─ AbstractAutoProxyCreator.postProcessAfterInitialization()
│        ├─ wrapIfNecessary()
│        │   ├─ 检查是否需要代理
│        │   ├─ 获取适用的Advisor
│        │   └─ createProxy()
│        │       ├─ 创建ProxyFactory
│        │       ├─ 决定JDK或CGLIB
│        │       ├─ 添加Advisor
│        │       └─ 生成代理对象
│        └─ 返回代理对象
│
└─ 注册到容器(注册的是代理对象)

关键: 容器中保存的是代理对象,不是原始对象!


💥 第二问:this调用问题的本质

问题演示

@Service
public class UserService {
    
    @Log
    public void method1() {
        System.out.println("method1 执行");
        this.method2();  // ← this调用
    }
    
    @Log
    public void method2() {
        System.out.println("method2 执行");
    }
}

测试:

userService.method1();

输出:

>>> 【日志】method1 开始
method1 执行
method2 执行
<<< 【日志】method1 结束

(method2的日志没有记录!)

原理分析

哈吉米画图解释:

客户端代码:
userService.method1();
    ↓
userService变量指向的是?
    ↓
代理对象(UserService$$Proxy)
    ↓
代理逻辑执行
    ├─ 日志Before
    ├─ 调用method1()
    │   └─ 进入原始对象的method1()
    │       └─ this.method2()  ← this是原始对象!
    │           └─ 直接调用原始对象的method2()
    │               └─ 没经过代理,AOP不生效! ❌
    └─ 日志After

核心问题:

// 在UserService类内部
this  →  原始对象(UserService@123// 不是
this  →  代理对象(UserService$$Proxy@456

解决方案

方案1:注入自己(推荐)

@Service
public class UserService {
    
    @Autowired
    @Lazy  // 避免循环依赖
    private UserService self;  // 注入自己(代理对象)
    
    @Log
    public void method1() {
        System.out.println("method1 执行");
        self.method2();  // ← 通过代理调用
    }
    
    @Log
    public void method2() {
        System.out.println("method2 执行");
    }
}

效果: ✅ AOP生效


方案2:使用AopContext

// 1. 启用exposeProxy
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration
public class AopConfig {
}

// 2. 获取当前代理对象
@Service
public class UserService {
    
    @Log
    public void method1() {
        System.out.println("method1 执行");
        
        // 获取当前代理对象
        UserService proxy = (UserService) AopContext.currentProxy();
        proxy.method2();  // 通过代理调用
    }
    
    @Log
    public void method2() {
        System.out.println("method2 执行");
    }
}

效果: ✅ AOP生效

缺点: 代码侵入性强,不推荐


方案3:拆分成两个类(最佳)

@Service
public class UserService {
    
    @Autowired
    private UserHelper userHelper;
    
    @Log
    public void method1() {
        System.out.println("method1 执行");
        userHelper.method2();  // 调用另一个Bean
    }
}

@Service
public class UserHelper {
    
    @Log
    public void method2() {
        System.out.println("method2 执行");
    }
}

效果: ✅ AOP生效,且代码结构更好


🐛 第三问:AOP失效的8种场景

场景1:方法不是public

哈吉米: "这是最容易忽略的!"

@Service
public class UserService {
    
    @Log
    private void createUser() {  // ← private方法
        System.out.println("创建用户");
    }
    
    @Log
    protected void updateUser() {  // ← protected方法
        System.out.println("更新用户");
    }
}

结果: ❌ AOP不生效

原因:

  • JDK代理:接口方法必须是public
  • CGLIB代理:只能代理public和protected方法,private无法代理

解决: 改成public方法


场景2:方法是final

@Service
public class UserService {
    
    @Log
    public final void createUser() {  // ← final方法
        System.out.println("创建用户");
    }
}

结果: ❌ AOP不生效(CGLIB代理时)

原因: CGLIB通过继承实现,无法重写final方法

解决: 去掉final修饰符


场景3:方法是static

@Service
public class UserService {
    
    @Log
    public static void createUser() {  // ← static方法
        System.out.println("创建用户");
    }
}

结果: ❌ AOP不生效

原因: 代理是对象级别的,static方法属于类,无法代理

解决: 改成实例方法


场景4:类内部调用(this调用)

@Service
public class UserService {
    
    @Log
    public void method1() {
        this.method2();  // ← this调用
    }
    
    @Log
    public void method2() {
        // AOP不生效
    }
}

结果: ❌ method2的AOP不生效

原因: 上面已经详细讲过

解决: 注入自己或拆分成两个类


场景5:Bean不是Spring管理的

// ❌ 没有@Service等注解
public class UserService {
    
    @Log
    public void createUser() {
        // AOP不生效
    }
}

// 手动new的对象
UserService userService = new UserService();
userService.createUser();  // AOP不生效

结果: ❌ AOP不生效

原因: AOP依赖Spring容器创建代理,手动new的对象不是代理

解决: 加上@Service,通过Spring容器获取


场景6:切点表达式写错

@Aspect
@Component
public class LogAspect {
    
    // 包名写错了
    @Before("execution(* com.example.services.*.*(..))")  // ← services多了s
    public void before() {
        // 不会执行
    }
}

// 实际包名是
package com.example.service;  // ← service没有s

结果: ❌ AOP不生效

原因: 切点表达式匹配不到任何方法

解决: 检查切点表达式是否正确


场景7:异步方法中的AOP

@Service
public class UserService {
    
    @Async
    @Log  // 日志切面
    public void createUser() {
        // 可能不生效或顺序错乱
    }
}

结果: ⚠️ 可能不生效

原因: @Async也是通过代理实现,两个代理可能冲突

解决:

  • 调整切面顺序(@Order)
  • 确保@Async的代理在外层

场景8:在构造器中调用

@Service
public class UserService {
    
    public UserService() {
        this.init();  // ← 构造器中调用
    }
    
    @Log
    public void init() {
        // AOP不生效
    }
}

结果: ❌ AOP不生效

原因: 构造器执行时,Bean还没创建完,代理还没生成

解决: 使用@PostConstruct

@Service
public class UserService {
    
    @PostConstruct  // 在Bean初始化后执行
    @Log
    public void init() {
        // AOP生效 ✅
    }
}

失效场景总结表

场景原因解决方案
private方法无法被代理改成public
final方法CGLIB无法重写去掉final
static方法类方法无法代理改成实例方法
this调用绕过代理对象注入自己/拆分类
非Spring管理不是代理对象加@Service
表达式错误匹配不到方法检查表达式
异步方法代理冲突调整顺序
构造器调用代理未创建用@PostConstruct

🎯 第四问:多个切面的执行顺序

场景

// 日志切面
@Aspect
@Component
public class LogAspect {
    
    @Around("@annotation(com.example.annotation.Log)")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(">>> 日志 - Before");
        Object result = pjp.proceed();
        System.out.println("<<< 日志 - After");
        return result;
    }
}

// 权限切面
@Aspect
@Component
public class SecurityAspect {
    
    @Around("@annotation(com.example.annotation.RequireRole)")
    public Object checkRole(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(">>> 权限 - Before");
        Object result = pjp.proceed();
        System.out.println("<<< 权限 - After");
        return result;
    }
}

// 性能切面
@Aspect
@Component
public class PerformanceAspect {
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(">>> 性能 - Before");
        Object result = pjp.proceed();
        System.out.println("<<< 性能 - After");
        return result;
    }
}

问题: 这三个切面的执行顺序是什么?


默认顺序(随机)

南北绿豆: "如果不指定顺序,执行顺序是不确定的!"

可能的输出:

>>> 性能 - Before
>>> 日志 - Before
>>> 权限 - Before
   [业务方法]
<<< 权限 - After
<<< 日志 - After
<<< 性能 - After

或者:

>>> 日志 - Before
>>> 性能 - Before
>>> 权限 - Before
   [业务方法]
<<< 权限 - After
<<< 性能 - After
<<< 日志 - After

使用@Order指定顺序

@Aspect
@Component
@Order(1)  // 优先级最高
public class SecurityAspect {
    // 权限校验应该最先执行
}

@Aspect
@Component
@Order(2)
public class LogAspect {
    // 日志其次
}

@Aspect
@Component
@Order(3)
public class PerformanceAspect {
    // 性能监控最后
}

执行顺序:

>>> 权限 - Before  (Order=1,最外层)
>>> 日志 - Before  (Order=2,中间层)
>>> 性能 - Before  (Order=3,最内层)
   [业务方法]
<<< 性能 - After   (Order=3,最内层)
<<< 日志 - After   (Order=2,中间层)
<<< 权限 - After   (Order=1,最外层)

规律:

  • Before: 数字小的先执行(1→2→3)
  • After: 数字小的后执行(3→2→1)
  • 类似洋葱模型🧅

实现Ordered接口

@Aspect
@Component
public class LogAspect implements Ordered {
    
    @Override
    public int getOrder() {
        return 2;  // 返回优先级
    }
    
    @Around("...")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        // ...
    }
}

🔬 第五问:源码深度分析

Advisor、Advice、Pointcut关系

阿西噶阿西: "在Spring AOP源码中,有三个核心概念。"

// Advice(通知)
public interface Advice {
    // 标记接口
}

// Pointcut(切点)
public interface Pointcut {
    ClassFilter getClassFilter();  // 类过滤器
    MethodMatcher getMethodMatcher();  // 方法匹配器
}

// Advisor(顾问)= Pointcut + Advice
public interface Advisor {
    Advice getAdvice();  // 获取通知
}

public interface PointcutAdvisor extends Advisor {
    Pointcut getPointcut();  // 获取切点
}

关系:

Advisor(顾问)
├─ Pointcut(在哪里增强)
└─ Advice(怎么增强)

@Aspect如何解析?

位置: org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

核心流程:

@Override
protected List<Advisor> findCandidateAdvisors() {
    // 1. 查找实现了Advisor接口的Bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    
    // 2. 查找@Aspect注解的Bean
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    
    return advisors;
}

buildAspectJAdvisors()方法:

public List<Advisor> buildAspectJAdvisors() {
    
    List<String> aspectNames = this.aspectBeanNames;
    
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                
                // 1. 获取所有Bean名称
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Object.class, true, false);
                
                // 2. 遍历所有Bean
                for (String beanName : beanNames) {
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    
                    if (beanType == null) {
                        continue;
                    }
                    
                    // 3. 检查是否有@Aspect注解
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        
                        // 4. 获取Aspect中的所有Advisor
                        List<Advisor> classAdvisors = 
                            this.advisorFactory.getAdvisors(factory);
                        
                        advisors.addAll(classAdvisors);
                    }
                }
                
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }
    
    // 返回缓存的Advisors
    // ...
}

getAdvisors()方法

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    
    List<Advisor> advisors = new ArrayList<>();
    
    // 1. 获取所有方法(排除@Pointcut方法)
    for (Method method : getAdvisorMethods(aspectClass)) {
        
        // 2. 为每个通知方法创建Advisor
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 
            advisors.size(), aspectName);
        
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    
    return advisors;
}

getAdvisor()方法

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, 
        MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {
    
    // 1. 获取切点
    AspectJExpressionPointcut expressionPointcut = getPointcut(
        candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    
    if (expressionPointcut == null) {
        return null;
    }
    
    // 2. 创建Advisor
    return new InstantiationModelAwarePointcutAdvisorImpl(
        expressionPointcut, 
        candidateAdviceMethod,
        this, 
        aspectInstanceFactory, 
        declarationOrderInAspect, 
        aspectName);
}

解析流程:

@Aspect类
    ↓
扫描所有方法
    ↓
找到@Before@After@Around等注解
    ↓
解析切点表达式
    ↓
创建对应的Advisor
    ↓
Advisor = Pointcut + Advice

💻 第六问:完整实战案例

案例:操作日志记录

需求: 记录用户的所有操作到数据库

1. 自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
    
    /**
     * 操作模块
     */
    String module() default "";
    
    /**
     * 操作类型
     */
    String type() default "";
    
    /**
     * 操作描述
     */
    String desc() default "";
}

2. 操作日志实体:

public class OperationLogEntity {
    
    private Long id;
    private String username;     // 操作人
    private String module;       // 模块
    private String type;         // 类型
    private String desc;         // 描述
    private String method;       // 方法名
    private String params;       // 参数
    private String result;       // 返回值
    private Long costTime;       // 耗时
    private Date createTime;     // 时间
    
    // Getter和Setter...
}

3. 操作日志切面:

@Aspect
@Component
@Slf4j
public class OperationLogAspect {
    
    @Autowired
    private OperationLogService operationLogService;
    
    /**
     * 环绕通知:记录操作日志
     */
    @Around("@annotation(operationLog)")
    public Object recordLog(ProceedingJoinPoint pjp, OperationLog operationLog) 
            throws Throwable {
        
        // 1. 构建日志对象
        OperationLogEntity logEntity = new OperationLogEntity();
        logEntity.setModule(operationLog.module());
        logEntity.setType(operationLog.type());
        logEntity.setDesc(operationLog.desc());
        
        // 2. 获取方法信息
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();
        logEntity.setMethod(className + "." + methodName);
        
        // 3. 获取参数
        Object[] args = pjp.getArgs();
        logEntity.setParams(JSON.toJSONString(args));
        
        // 4. 获取当前用户(从SecurityContext或Session)
        String username = getCurrentUsername();
        logEntity.setUsername(username);
        
        // 5. 执行方法,记录耗时
        long start = System.currentTimeMillis();
        Object result = null;
        
        try {
            result = pjp.proceed();
            
            // 6. 记录返回值
            logEntity.setResult(JSON.toJSONString(result));
            
        } catch (Exception e) {
            // 7. 记录异常
            logEntity.setResult("异常:" + e.getMessage());
            throw e;
        } finally {
            long end = System.currentTimeMillis();
            logEntity.setCostTime(end - start);
            logEntity.setCreateTime(new Date());
            
            // 8. 异步保存日志(不影响主流程)
            CompletableFuture.runAsync(() -> {
                operationLogService.save(logEntity);
            });
        }
        
        return result;
    }
    
    private String getCurrentUsername() {
        // 实际从SecurityContext获取
        return "admin";
    }
}

4. 使用:

@Service
public class UserService {
    
    @OperationLog(
        module = "用户管理",
        type = "创建",
        desc = "创建新用户"
    )
    public User createUser(User user) {
        // 保存用户
        userDao.save(user);
        return user;
    }
    
    @OperationLog(
        module = "用户管理",
        type = "删除",
        desc = "删除用户"
    )
    public void deleteUser(Long userId) {
        userDao.delete(userId);
    }
    
    @OperationLog(
        module = "用户管理",
        type = "更新",
        desc = "更新用户信息"
    )
    public User updateUser(User user) {
        userDao.update(user);
        return user;
    }
}

5. 测试:

userService.createUser(new User("张三", "zhangsan@example.com"));

数据库中的日志:

SELECT * FROM operation_log;

| id | username | module | type | desc | method | params | result | costTime | createTime |
|----|----------|--------|------|------|--------|--------|--------|----------|------------|
| 1  | admin    | 用户管理 | 创建  | 创建新用户 | UserService.createUser | [{"name":"张三","email":"zhangsan@example.com"}] | {"id":1,"name":"张三"} | 15 | 2024-01-20 10:30:00 |

完美!


🎉 系列总结

三篇文章回顾

第一篇:代理模式与动态代理

  • ✅ 静态代理vs动态代理
  • ✅ JDK动态代理原理
  • ✅ CGLIB动态代理原理
  • ✅ 两者对比与选择
  • ✅ 手写简易版代理框架

第二篇:@Aspect注解与通知类型

  • ✅ AOP核心概念(6个)
  • ✅ 5种通知类型详解
  • ✅ 通知执行顺序
  • ✅ 切点表达式
  • ✅ 实战案例(日志、权限、性能)

第三篇:代理创建时机与失效场景

  • ✅ 代理创建时机(BeanPostProcessor后置处理)
  • ✅ AbstractAutoProxyCreator源码
  • ✅ AOP失效的8种场景
  • ✅ this调用问题详解
  • ✅ 多个切面的执行顺序
  • ✅ 完整的操作日志案例

学习路线

Level 1: 会用AOP 
└─ 知道@Aspect、@Before等注解

Level 2: 理解原理 ⭐⭐
└─ 理解动态代理

Level 3: 掌握通知 ⭐⭐⭐
└─ 掌握5种通知类型和切点表达式

Level 4: 深入源码 ⭐⭐⭐⭐
└─ 理解代理创建时机和过程

Level 5: 精通AOP ⭐⭐⭐⭐⭐
└─ 能解决所有AOP问题,设计复杂切面

恭喜你,完成这个系列,你已经达到Level 4! 🎉


核心知识图谱

Spring AOP
├─ 底层技术
│   ├─ JDK动态代理(基于接口)
│   └─ CGLIB代理(基于继承)
│
├─ 核心概念
│   ├─ 切面(Aspect)
│   ├─ 切点(Pointcut)
│   ├─ 通知(Advice)
│   ├─ 连接点(Join Point)
│   ├─ 目标对象(Target)
│   └─ 织入(Weaving)
│
├─ 通知类型
│   ├─ @Before
│   ├─ @After
│   ├─ @AfterReturning
│   ├─ @AfterThrowing
│   └─ @Around
│
├─ 代理创建
│   ├─ BeanPostProcessor后置处理
│   ├─ AbstractAutoProxyCreator
│   └─ ProxyFactory
│
└─ 常见问题
    ├─ this调用失效
    ├─ private/final/static方法失效
    ├─ 非Spring管理失效
    └─ 切面顺序控制

💡 最佳实践建议

1. 切面设计原则

// ✅ 推荐:职责单一
@Aspect
@Component
public class LogAspect {
    // 只负责日志
}

@Aspect
@Component
public class SecurityAspect {
    // 只负责权限
}

// ❌ 不推荐:职责混乱
@Aspect
@Component
public class CommonAspect {
    // 又做日志,又做权限,又做性能监控...
}

2. 切点表达式优化

// ✅ 推荐:使用@Pointcut复用
@Aspect
@Component
public class LogAspect {
    
    @Pointcut("@annotation(com.example.annotation.Log)")
    public void logMethods() {}
    
    @Before("logMethods()")  // 复用
    public void before() {}
    
    @After("logMethods()")  // 复用
    public void after() {}
}

// ❌ 不推荐:重复写表达式
@Before("@annotation(com.example.annotation.Log)")
@After("@annotation(com.example.annotation.Log)")

3. 通知类型选择

// ✅ 推荐:语义清晰
@Before("...")  // 只需要前置处理
public void before() {}

// ❌ 不推荐:滥用@Around
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // 前置处理
    return pjp.proceed();  // 后置什么都不做
}

4. 异常处理

// ✅ 推荐:Around中完整处理异常
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    try {
        return pjp.proceed();
    } catch (BusinessException e) {
        // 业务异常:记录日志,继续抛出
        log.error("业务异常", e);
        throw e;
    } catch (Exception e) {
        // 系统异常:记录日志,包装后抛出
        log.error("系统异常", e);
        throw new SystemException("系统错误", e);
    }
}

5. 性能考虑

// ✅ 推荐:避免在切面中执行耗时操作
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    
    // 耗时操作异步执行
    CompletableFuture.runAsync(() -> {
        saveLog(...);  // 异步保存日志
    });
    
    return pjp.proceed();
}

// ❌ 不推荐:同步执行耗时操作
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    saveLog(...);  // 同步保存,影响性能
    return pjp.proceed();
}

💬 写在最后

从动态代理到@Aspect注解,再到源码剖析,我们完整学习了Spring AOP的全部核心内容:

  • 🎭 理解了JDK和CGLIB两种代理方式
  • 🎯 掌握了5种通知类型和切点表达式
  • 🔬 深入了代理创建时机和源码实现
  • 🐛 学会了AOP失效场景的排查和解决
  • 💻 完成了多个实战案例

这个系列到这里就完结了!

如果这个系列对你有帮助,请:

  • 👍 点赞支持
  • ⭐ 收藏备用
  • 🔄 转发分享
  • 💬 评论交流

感谢一路陪伴,期待下个系列再见! 👋