Skip to content

Spring AOP 详解

概述

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。Spring AOP 是 Spring 框架的核心特性之一,它提供了声明式的方式来实现面向切面编程,主要用于处理系统中的横切关注点,如日志记录、事务管理、安全检查、性能监控等。

目录

  1. AOP 基本概念
  2. AOP 核心术语
  3. Spring AOP 实现原理
  4. 切点表达式
  5. 通知类型
  6. 切面定义
  7. 代理机制
  8. AOP 配置方式
  9. 实际应用场景
  10. 高级特性
  11. 性能考虑
  12. 最佳实践

AOP 基本概念

什么是面向切面编程

面向切面编程(AOP)是对面向对象编程(OOP)的补充,它通过预编译方式和运行期动态代理实现程序功能的统一维护。AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度。

横切关注点

横切关注点是指那些影响多个类的公共行为,这些行为通常难以通过传统的面向对象方式进行模块化。常见的横切关注点包括:

  • 日志记录:记录方法调用、参数、返回值等
  • 事务管理:数据库事务的开启、提交、回滚
  • 安全检查:权限验证、身份认证
  • 性能监控:方法执行时间统计
  • 缓存管理:缓存的读取、更新、清除
  • 异常处理:统一的异常处理逻辑

传统方式 vs AOP 方式

传统方式

java
public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    
    public User createUser(User user) {
        // 日志记录
        logger.info("开始创建用户: {}", user.getUsername());
        
        // 参数验证
        if (user == null || user.getUsername() == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }
        
        // 性能监控
        long startTime = System.currentTimeMillis();
        
        try {
            // 核心业务逻辑
            User savedUser = userRepository.save(user);
            
            // 日志记录
            logger.info("用户创建成功: {}", savedUser.getId());
            
            return savedUser;
        } catch (Exception e) {
            // 异常处理
            logger.error("用户创建失败", e);
            throw e;
        } finally {
            // 性能监控
            long endTime = System.currentTimeMillis();
            logger.info("方法执行时间: {}ms", endTime - startTime);
        }
    }
}

AOP 方式

java
@Service
public class UserService {
    
    @Loggable
    @PerformanceMonitor
    @ValidateParams
    public User createUser(User user) {
        // 只关注核心业务逻辑
        return userRepository.save(user);
    }
}

// 日志切面
@Aspect
@Component
public class LoggingAspect {
    
    @Around("@annotation(Loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 日志逻辑
    }
}

AOP 核心术语

1. 切面(Aspect)

切面是横切关注点的模块化,它将散布在多个类中的公共行为封装到一个可重用的模块中。

java
@Aspect
@Component
public class LoggingAspect {
    // 切面定义
}

2. 连接点(Join Point)

连接点是程序执行过程中能够应用通知的所有点,如方法调用、异常抛出等。在 Spring AOP 中,连接点总是方法的执行。

java
// 这些都是连接点
public class UserService {
    public User findById(Long id) { ... }     // 连接点
    public User save(User user) { ... }       // 连接点
    public void delete(Long id) { ... }       // 连接点
}

3. 切点(Pointcut)

切点是连接点的集合,它定义了通知应该在何处执行。

java
@Pointcut("execution(* com.example.service.*.*(..))") // 切点表达式
public void serviceLayer() {}

@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}

4. 通知(Advice)

通知是切面在特定连接点执行的动作。Spring AOP 支持五种类型的通知:

  • 前置通知(Before):在连接点之前执行
  • 后置通知(After):在连接点之后执行
  • 返回通知(AfterReturning):在连接点正常返回后执行
  • 异常通知(AfterThrowing):在连接点抛出异常后执行
  • 环绕通知(Around):围绕连接点执行

5. 目标对象(Target Object)

目标对象是被一个或多个切面通知的对象,也被称为被通知对象。

java
@Service
public class UserService { // 这是目标对象
    public User findById(Long id) {
        return userRepository.findById(id);
    }
}

6. AOP 代理(AOP Proxy)

AOP 代理是 AOP 框架创建的对象,用于实现切面契约。在 Spring 中,AOP 代理可以是 JDK 动态代理或 CGLIB 代理。

7. 织入(Weaving)

织入是将切面与目标对象连接起来创建通知对象的过程。Spring AOP 在运行时进行织入。


Spring AOP 实现原理

代理模式

Spring AOP 基于代理模式实现,它在运行时创建目标对象的代理,在代理中织入切面逻辑。

java
// 目标接口
public interface UserService {
    User findById(Long id);
    User save(User user);
}

// 目标实现
@Service
public class UserServiceImpl implements UserService {
    
    @Override
    public User findById(Long id) {
        return userRepository.findById(id);
    }
    
    @Override
    public User save(User user) {
        return userRepository.save(user);
    }
}

// Spring 创建的代理对象(概念示意)
public class UserServiceProxy implements UserService {
    private UserService target;
    private List<Advisor> advisors;
    
    @Override
    public User findById(Long id) {
        // 前置通知
        for (Advisor advisor : advisors) {
            advisor.getAdvice().before();
        }
        
        try {
            // 调用目标方法
            User result = target.findById(id);
            
            // 返回通知
            for (Advisor advisor : advisors) {
                advisor.getAdvice().afterReturning(result);
            }
            
            return result;
        } catch (Exception e) {
            // 异常通知
            for (Advisor advisor : advisors) {
                advisor.getAdvice().afterThrowing(e);
            }
            throw e;
        } finally {
            // 后置通知
            for (Advisor advisor : advisors) {
                advisor.getAdvice().after();
            }
        }
    }
}

两种代理方式

1. JDK 动态代理

java
// 当目标对象实现了接口时,Spring 默认使用 JDK 动态代理
public interface UserService {
    User findById(Long id);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public User findById(Long id) {
        return userRepository.findById(id);
    }
}

2. CGLIB 代理

java
// 当目标对象没有实现接口时,Spring 使用 CGLIB 代理
@Service
public class UserService { // 没有实现接口
    public User findById(Long id) {
        return userRepository.findById(id);
    }
}

// 强制使用 CGLIB 代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
public class AopConfig {
}

切点表达式

表达式语法

Spring AOP 使用 AspectJ 的切点表达式语言来定义切点。

1. execution 表达式

java
// 语法:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

@Pointcut("execution(public * com.example.service.*.*(..))") // 所有 service 包下的 public 方法
public void publicServiceMethods() {}

@Pointcut("execution(* com.example.service.UserService.find*(..))")
public void userServiceFindMethods() {} // UserService 中以 find 开头的方法

@Pointcut("execution(User com.example.service.UserService.*(..))")
public void userServiceReturningUser() {} // 返回 User 类型的方法

@Pointcut("execution(* com.example.service.*.*(Long, ..))")
public void methodsWithLongFirstParam() {} // 第一个参数为 Long 类型的方法

2. within 表达式

java
@Pointcut("within(com.example.service.*)") // service 包下的所有类
public void servicePackage() {}

@Pointcut("within(com.example.service..*)") // service 包及其子包下的所有类
public void servicePackageAndSubpackages() {}

@Pointcut("within(com.example.service.UserService)") // 特定类
public void userServiceClass() {}

3. this 和 target 表达式

java
@Pointcut("this(com.example.service.UserService)") // 代理对象是 UserService 类型
public void thisUserService() {}

@Pointcut("target(com.example.service.UserService)") // 目标对象是 UserService 类型
public void targetUserService() {}

4. args 表达式

java
@Pointcut("args(Long)") // 只有一个 Long 类型参数的方法
public void singleLongArg() {}

@Pointcut("args(Long, String)") // 两个参数:Long 和 String
public void longAndStringArgs() {}

@Pointcut("args(Long, ..)") // 第一个参数是 Long,后面可以有任意参数
public void longFirstArg() {}

5. 注解表达式

java
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {} // 标注了 @Transactional 的方法

@Pointcut("@within(org.springframework.stereotype.Service)")
public void serviceClasses() {} // 标注了 @Service 的类中的所有方法

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryBeans() {} // 目标对象的类标注了 @Repository

@Pointcut("@args(com.example.annotation.Validated)")
public void validatedArgs() {} // 参数标注了 @Validated

组合表达式

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

@Pointcut("execution(* com.example.repository.*.*(..))")
public void repositoryLayer() {}

@Pointcut("serviceLayer() || repositoryLayer()") // 或操作
public void serviceOrRepository() {}

@Pointcut("serviceLayer() && @annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalServiceMethods() {} // 与操作

@Pointcut("serviceLayer() && !execution(* com.example.service.*.get*(..))")
public void nonGetterServiceMethods() {} // 非操作

通知类型

1. 前置通知(@Before)

在目标方法执行之前执行。

java
@Aspect
@Component
public class LoggingAspect {
    
    @Before("execution(* com.example.service.*.*(..))") 
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        logger.info("方法 {} 开始执行,参数:{}", methodName, Arrays.toString(args));
    }
    
    @Before("@annotation(loggable)")
    public void logBeforeAnnotated(JoinPoint joinPoint, Loggable loggable) {
        logger.info("执行标注了 @Loggable 的方法:{}", joinPoint.getSignature().getName());
    }
}

2. 后置通知(@After)

在目标方法执行之后执行,无论方法是否抛出异常。

java
@Aspect
@Component
public class CleanupAspect {
    
    @After("execution(* com.example.service.*.*(..))") 
    public void cleanup(JoinPoint joinPoint) {
        logger.info("方法 {} 执行完成,进行清理工作", joinPoint.getSignature().getName());
        // 清理资源、重置状态等
    }
}

3. 返回通知(@AfterReturning)

在目标方法正常返回后执行。

java
@Aspect
@Component
public class AuditAspect {
    
    @AfterReturning(pointcut = "execution(* com.example.service.UserService.save(..))", 
                    returning = "result")
    public void auditUserSave(JoinPoint joinPoint, User result) {
        logger.info("用户保存成功:{}", result.getId());
        // 记录审计日志
        auditService.recordUserCreation(result);
    }
    
    @AfterReturning(pointcut = "@annotation(Cacheable)", returning = "result")
    public void cacheResult(JoinPoint joinPoint, Object result) {
        String key = generateCacheKey(joinPoint);
        cacheManager.put(key, result);
    }
}

4. 异常通知(@AfterThrowing)

在目标方法抛出异常后执行。

java
@Aspect
@Component
public class ExceptionHandlingAspect {
    
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", 
                   throwing = "ex")
    public void handleException(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        logger.error("方法 {} 执行异常", methodName, ex);
        
        // 发送告警通知
        alertService.sendAlert("方法执行异常: " + methodName, ex);
    }
    
    @AfterThrowing(pointcut = "@annotation(Retryable)", throwing = "ex")
    public void handleRetryableException(JoinPoint joinPoint, Exception ex) {
        // 处理可重试的异常
        retryService.scheduleRetry(joinPoint, ex);
    }
}

5. 环绕通知(@Around)

围绕目标方法执行,是最强大的通知类型。

java
@Aspect
@Component
public class PerformanceAspect {
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        
        try {
            logger.info("方法 {} 开始执行", methodName);
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            long endTime = System.currentTimeMillis();
            logger.info("方法 {} 执行成功,耗时:{}ms", methodName, endTime - startTime);
            
            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            logger.error("方法 {} 执行失败,耗时:{}ms", methodName, endTime - startTime, e);
            throw e;
        }
    }
    
    @Around("@annotation(rateLimited)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimited rateLimited) throws Throwable {
        String key = generateRateLimitKey(joinPoint);
        
        if (rateLimiter.tryAcquire(key, rateLimited.value())) {
            return joinPoint.proceed();
        } else {
            throw new RateLimitExceededException("请求过于频繁");
        }
    }
}

通知的执行顺序

java
@Aspect
@Component
@Order(1) // 设置切面执行顺序
public class FirstAspect {
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("First - Around Before");
        try {
            Object result = joinPoint.proceed();
            System.out.println("First - Around After Returning");
            return result;
        } catch (Exception e) {
            System.out.println("First - Around After Throwing");
            throw e;
        } finally {
            System.out.println("First - Around After (finally)");
        }
    }
}

@Aspect
@Component
@Order(2)
public class SecondAspect {
    
    @Before("execution(* com.example.service.*.*(..))") 
    public void before() {
        System.out.println("Second - Before");
    }
    
    @After("execution(* com.example.service.*.*(..))") 
    public void after() {
        System.out.println("Second - After");
    }
}

// 执行顺序:
// First - Around Before
// Second - Before
// [目标方法执行]
// Second - After
// First - Around After Returning
// First - Around After (finally)

切面定义

基本切面

java
@Aspect
@Component
public class BasicAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(BasicAspect.class);
    
    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))") 
    public void serviceLayer() {}
    
    // 前置通知
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("执行方法:{}", joinPoint.getSignature().getName());
    }
    
    // 环绕通知
    @Around("serviceLayer()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            logger.info("方法执行成功,耗时:{}ms", endTime - startTime);
            return result;
        } catch (Exception e) {
            logger.error("方法执行异常", e);
            throw e;
        }
    }
}

参数化切面

java
@Aspect
@Component
public class ParameterizedAspect {
    
    // 获取方法参数
    @Before("execution(* com.example.service.UserService.*(..)) && args(id,..)") 
    public void logUserId(Long id) {
        logger.info("操作用户ID:{}", id);
    }
    
    // 获取注解参数
    @Around("@annotation(cacheable)")
    public Object handleCacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String cacheName = cacheable.value();
        String key = generateKey(joinPoint);
        
        // 先查缓存
        Object cached = cacheManager.get(cacheName, key);
        if (cached != null) {
            return cached;
        }
        
        // 执行方法并缓存结果
        Object result = joinPoint.proceed();
        cacheManager.put(cacheName, key, result);
        return result;
    }
    
    // 获取返回值
    @AfterReturning(pointcut = "execution(* com.example.service.UserService.save(..))", 
                    returning = "user")
    public void auditUserSave(User user) {
        auditService.recordUserCreation(user.getId(), user.getUsername());
    }
}

条件切面

java
@Aspect
@Component
@ConditionalOnProperty(name = "app.aop.logging.enabled", havingValue = "true")
public class ConditionalLoggingAspect {
    
    @Around("@annotation(Loggable)")
    public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        // 只有在配置启用时才会生效
        return joinPoint.proceed();
    }
}

@Aspect
@Component
@Profile("!production") // 非生产环境才启用
public class DebugAspect {
    
    @Before("execution(* com.example.service.*.*(..))") 
    public void debugLog(JoinPoint joinPoint) {
        logger.debug("调试信息:{}", joinPoint.getSignature());
    }
}

代理机制

JDK 动态代理 vs CGLIB 代理

JDK 动态代理

java
// 接口
public interface UserService {
    User findById(Long id);
    User save(User user);
}

// 实现类
@Service
public class UserServiceImpl implements UserService {
    
    @Override
    public User findById(Long id) {
        return userRepository.findById(id);
    }
    
    @Override
    public User save(User user) {
        return userRepository.save(user);
    }
}

// Spring 会创建 JDK 动态代理
@Autowired
private UserService userService; // 这里注入的是代理对象

CGLIB 代理

java
// 没有接口的类
@Service
public class UserService {
    
    public User findById(Long id) {
        return userRepository.findById(id);
    }
    
    public User save(User user) {
        return userRepository.save(user);
    }
}

// Spring 会创建 CGLIB 代理
@Autowired
private UserService userService; // 这里注入的是 CGLIB 代理对象

代理的限制

1. 内部方法调用

java
@Service
public class UserService {
    
    @Transactional
    public User saveUser(User user) {
        // 这个调用不会触发 AOP,因为是内部调用
        return this.doSave(user);
    }
    
    @Transactional
    public User doSave(User user) {
        return userRepository.save(user);
    }
}

// 解决方案1:注入自己
@Service
public class UserService {
    
    @Autowired
    private UserService self; // 注入代理对象
    
    @Transactional
    public User saveUser(User user) {
        // 通过代理调用,会触发 AOP
        return self.doSave(user);
    }
    
    @Transactional
    public User doSave(User user) {
        return userRepository.save(user);
    }
}

// 解决方案2:使用 AopContext
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class UserService {
    
    @Transactional
    public User saveUser(User user) {
        UserService proxy = (UserService) AopContext.currentProxy();
        return proxy.doSave(user);
    }
    
    @Transactional
    public User doSave(User user) {
        return userRepository.save(user);
    }
}

2. final 方法和类

java
// CGLIB 无法代理 final 类
public final class FinalUserService {
    public User findById(Long id) {
        return userRepository.findById(id);
    }
}

// CGLIB 无法代理 final 方法
@Service
public class UserService {
    
    public final User findById(Long id) { // final 方法不会被代理
        return userRepository.findById(id);
    }
    
    public User save(User user) { // 普通方法会被代理
        return userRepository.save(user);
    }
}

强制使用 CGLIB

java
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB
public class AopConfig {
}

// 或者在 application.properties 中配置
// spring.aop.proxy-target-class=true

AOP 配置方式

1. 注解配置(推荐)

java
@Configuration
@EnableAspectJAutoProxy // 启用 AspectJ 自动代理
@ComponentScan(basePackages = "com.example")
public class AopConfig {
}

@Aspect
@Component
public class LoggingAspect {
    
    @Pointcut("execution(* com.example.service.*.*(..))") 
    public void serviceLayer() {}
    
    @Around("serviceLayer()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 切面逻辑
        return joinPoint.proceed();
    }
}

2. XML 配置

xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 启用 AspectJ 自动代理 -->
    <aop:aspectj-autoproxy/>
    
    <!-- 定义切面 -->
    <bean id="loggingAspect" class="com.example.aspect.LoggingAspect"/>
    
    <!-- AOP 配置 -->
    <aop:config>
        <aop:aspect ref="loggingAspect">
            <aop:pointcut id="serviceLayer" 
                         expression="execution(* com.example.service.*.*(..))"/>
            
            <aop:before pointcut-ref="serviceLayer" method="logBefore"/>
            <aop:after pointcut-ref="serviceLayer" method="logAfter"/>
            <aop:around pointcut-ref="serviceLayer" method="logAround"/>
        </aop:aspect>
    </aop:config>
    
</beans>

3. Java 配置

java
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
    
    @Bean
    public PerformanceAspect performanceAspect() {
        return new PerformanceAspect();
    }
    
    @Bean
    public DefaultPointcutAdvisor loggingAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.example.service.*.*(..))");
        
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(new LoggingInterceptor());
        
        return advisor;
    }
}

实际应用场景

1. 日志记录

java
@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    @Around("@annotation(Loggable)")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        logger.info("[{}] 方法 {} 开始执行,参数:{}", className, methodName, Arrays.toString(args));
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            
            logger.info("[{}] 方法 {} 执行成功,耗时:{}ms,返回值:{}", 
                       className, methodName, endTime - startTime, result);
            
            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            
            logger.error("[{}] 方法 {} 执行失败,耗时:{}ms,异常:{}", 
                        className, methodName, endTime - startTime, e.getMessage(), e);
            
            throw e;
        }
    }
}

// 使用
@Service
public class UserService {
    
    @Loggable
    public User createUser(User user) {
        return userRepository.save(user);
    }
}

2. 性能监控

java
@Aspect
@Component
public class PerformanceMonitoringAspect {
    
    private final MeterRegistry meterRegistry;
    
    public PerformanceMonitoringAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Around("@annotation(PerformanceMonitor)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        
        Timer.Sample sample = Timer.start(meterRegistry);
        
        try {
            Object result = joinPoint.proceed();
            
            // 记录成功执行的指标
            sample.stop(Timer.builder("method.execution.time")
                    .tag("class", className)
                    .tag("method", methodName)
                    .tag("status", "success")
                    .register(meterRegistry));
            
            // 增加成功计数
            Counter.builder("method.execution.count")
                    .tag("class", className)
                    .tag("method", methodName)
                    .tag("status", "success")
                    .register(meterRegistry)
                    .increment();
            
            return result;
        } catch (Exception e) {
            // 记录失败执行的指标
            sample.stop(Timer.builder("method.execution.time")
                    .tag("class", className)
                    .tag("method", methodName)
                    .tag("status", "error")
                    .register(meterRegistry));
            
            // 增加失败计数
            Counter.builder("method.execution.count")
                    .tag("class", className)
                    .tag("method", methodName)
                    .tag("status", "error")
                    .tag("exception", e.getClass().getSimpleName())
                    .register(meterRegistry)
                    .increment();
            
            throw e;
        }
    }
}

3. 缓存管理

java
@Aspect
@Component
public class CacheAspect {
    
    private final CacheManager cacheManager;
    
    public CacheAspect(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }
    
    @Around("@annotation(cacheable)")
    public Object handleCacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String cacheName = cacheable.value();
        String key = generateCacheKey(joinPoint, cacheable.key());
        
        // 查询缓存
        Cache cache = cacheManager.getCache(cacheName);
        if (cache != null) {
            Cache.ValueWrapper wrapper = cache.get(key);
            if (wrapper != null) {
                logger.debug("缓存命中:{}", key);
                return wrapper.get();
            }
        }
        
        // 执行方法
        Object result = joinPoint.proceed();
        
        // 缓存结果
        if (cache != null && result != null) {
            cache.put(key, result);
            logger.debug("缓存更新:{}", key);
        }
        
        return result;
    }
    
    @AfterReturning(pointcut = "@annotation(cacheEvict)", returning = "result")
    public void handleCacheEvict(JoinPoint joinPoint, CacheEvict cacheEvict, Object result) {
        String cacheName = cacheEvict.value();
        
        if (cacheEvict.allEntries()) {
            // 清空整个缓存
            Cache cache = cacheManager.getCache(cacheName);
            if (cache != null) {
                cache.clear();
                logger.debug("清空缓存:{}", cacheName);
            }
        } else {
            // 清除特定缓存项
            String key = generateCacheKey(joinPoint, cacheEvict.key());
            Cache cache = cacheManager.getCache(cacheName);
            if (cache != null) {
                cache.evict(key);
                logger.debug("清除缓存:{}", key);
            }
        }
    }
    
    private String generateCacheKey(JoinPoint joinPoint, String keyExpression) {
        if (StringUtils.hasText(keyExpression)) {
            // 使用 SpEL 表达式解析 key
            return spelExpressionParser.parseExpression(keyExpression)
                    .getValue(new MethodBasedEvaluationContext(joinPoint), String.class);
        } else {
            // 默认使用方法名和参数生成 key
            return joinPoint.getSignature().getName() + "_" + 
                   Arrays.hashCode(joinPoint.getArgs());
        }
    }
}

4. 安全检查

java
@Aspect
@Component
public class SecurityAspect {
    
    private final AuthenticationService authenticationService;
    private final AuthorizationService authorizationService;
    
    @Before("@annotation(requiresAuth)")
    public void checkAuthentication(JoinPoint joinPoint, RequiresAuth requiresAuth) {
        // 检查用户是否已认证
        if (!authenticationService.isAuthenticated()) {
            throw new AuthenticationException("用户未认证");
        }
        
        // 检查用户角色
        String[] requiredRoles = requiresAuth.roles();
        if (requiredRoles.length > 0) {
            boolean hasRole = authorizationService.hasAnyRole(requiredRoles);
            if (!hasRole) {
                throw new AuthorizationException("用户权限不足");
            }
        }
        
        // 检查用户权限
        String[] requiredPermissions = requiresAuth.permissions();
        if (requiredPermissions.length > 0) {
            boolean hasPermission = authorizationService.hasAnyPermission(requiredPermissions);
            if (!hasPermission) {
                throw new AuthorizationException("用户权限不足");
            }
        }
    }
    
    @Around("@annotation(rateLimited)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimited rateLimited) throws Throwable {
        String userId = authenticationService.getCurrentUserId();
        String methodName = joinPoint.getSignature().getName();
        String key = "rate_limit:" + userId + ":" + methodName;
        
        // 检查速率限制
        if (!rateLimiter.tryAcquire(key, rateLimited.value(), rateLimited.timeUnit())) {
            throw new RateLimitExceededException("请求过于频繁,请稍后再试");
        }
        
        return joinPoint.proceed();
    }
}

// 使用
@Service
public class UserService {
    
    @RequiresAuth(roles = {"ADMIN", "USER_MANAGER"})
    public User createUser(User user) {
        return userRepository.save(user);
    }
    
    @RateLimited(value = 10, timeUnit = TimeUnit.MINUTES)
    public List<User> searchUsers(String keyword) {
        return userRepository.findByKeyword(keyword);
    }
}

5. 事务管理

java
@Aspect
@Component
public class TransactionAspect {
    
    private final PlatformTransactionManager transactionManager;
    
    @Around("@annotation(transactional)")
    public Object handleTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
        TransactionDefinition definition = new DefaultTransactionDefinition(
                transactional.propagation().value());
        
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            Object result = joinPoint.proceed();
            transactionManager.commit(status);
            return result;
        } catch (Exception e) {
            // 检查是否需要回滚
            boolean shouldRollback = shouldRollback(e, transactional.rollbackFor(), 
                                                   transactional.noRollbackFor());
            
            if (shouldRollback) {
                transactionManager.rollback(status);
            } else {
                transactionManager.commit(status);
            }
            
            throw e;
        }
    }
    
    private boolean shouldRollback(Exception e, Class<? extends Throwable>[] rollbackFor,
                                  Class<? extends Throwable>[] noRollbackFor) {
        // 实现回滚逻辑判断
        return true;
    }
}

高级特性

1. 引入(Introduction)

引入允许为现有的类添加新的方法和属性。

java
// 定义要引入的接口
public interface Auditable {
    void setCreatedDate(Date date);
    Date getCreatedDate();
    void setLastModified(Date date);
    Date getLastModified();
}

// 引入的实现
public class AuditableImpl implements Auditable {
    private Date createdDate;
    private Date lastModified;
    
    @Override
    public void setCreatedDate(Date date) {
        this.createdDate = date;
    }
    
    @Override
    public Date getCreatedDate() {
        return createdDate;
    }
    
    @Override
    public void setLastModified(Date date) {
        this.lastModified = date;
    }
    
    @Override
    public Date getLastModified() {
        return lastModified;
    }
}

// 切面定义引入
@Aspect
@Component
public class AuditAspect {
    
    @DeclareParents(value = "com.example.domain.*", defaultImpl = AuditableImpl.class)
    public static Auditable auditable;
    
    @Before("execution(* com.example.repository.*.save(..)) && this(auditable)")
    public void auditSave(JoinPoint joinPoint, Auditable auditable) {
        Date now = new Date();
        if (auditable.getCreatedDate() == null) {
            auditable.setCreatedDate(now);
        }
        auditable.setLastModified(now);
    }
}

// 使用
@Entity
public class User {
    private Long id;
    private String username;
    // 其他字段
}

// 在运行时,User 对象可以转换为 Auditable
User user = userService.findById(1L);
Auditable auditableUser = (Auditable) user;
Date createdDate = auditableUser.getCreatedDate();

2. 切面继承

java
// 基础切面
@Aspect
public abstract class BaseLoggingAspect {
    
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    
    @Around("loggingPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        logger.info("方法 {} 开始执行", methodName);
        
        try {
            Object result = joinPoint.proceed();
            logger.info("方法 {} 执行成功", methodName);
            return result;
        } catch (Exception e) {
            logger.error("方法 {} 执行失败", methodName, e);
            throw e;
        }
    }
    
    // 抽象切点,由子类实现
    @Pointcut
    public abstract void loggingPointcut();
}

// 服务层日志切面
@Aspect
@Component
public class ServiceLoggingAspect extends BaseLoggingAspect {
    
    @Override
    @Pointcut("execution(* com.example.service.*.*(..))") 
    public void loggingPointcut() {
    }
}

// 控制器层日志切面
@Aspect
@Component
public class ControllerLoggingAspect extends BaseLoggingAspect {
    
    @Override
    @Pointcut("execution(* com.example.controller.*.*(..))") 
    public void loggingPointcut() {
    }
}

3. 动态切面

java
@Component
public class DynamicAspectRegistry {
    
    private final List<DynamicAspect> aspects = new ArrayList<>();
    
    public void registerAspect(DynamicAspect aspect) {
        aspects.add(aspect);
    }
    
    public void unregisterAspect(DynamicAspect aspect) {
        aspects.remove(aspect);
    }
    
    @EventListener
    public void handleConfigChange(ConfigChangeEvent event) {
        // 根据配置变化动态注册或注销切面
        if (event.isLoggingEnabled()) {
            registerAspect(new LoggingAspect());
        } else {
            unregisterAspect(findAspectByType(LoggingAspect.class));
        }
    }
}

@Aspect
@Component
public class DynamicExecutionAspect {
    
    @Autowired
    private DynamicAspectRegistry aspectRegistry;
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object dynamicExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        // 动态执行注册的切面
        for (DynamicAspect aspect : aspectRegistry.getAspects()) {
            if (aspect.matches(joinPoint)) {
                return aspect.execute(joinPoint);
            }
        }
        
        return joinPoint.proceed();
    }
}

性能考虑

1. 切点表达式优化

java
// 低效:过于宽泛的切点
@Pointcut("execution(* *.*(..))")
public void allMethods() {} // 会匹配所有方法,性能很差

// 高效:精确的切点
@Pointcut("execution(* com.example.service.*Service.*(..))")
public void serviceMethods() {} // 只匹配特定包下的 Service 类

// 高效:使用注解切点
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {} // 只匹配标注了注解的方法

// 高效:组合使用
@Pointcut("within(com.example.service..*) && @annotation(Loggable)")
public void loggableServiceMethods() {} // 精确匹配

2. 避免过度使用 AOP

java
// 不推荐:为简单逻辑使用 AOP
@Aspect
@Component
public class SimpleValidationAspect {
    
    @Before("execution(* com.example.service.*.*(..))")
    public void validateNotNull(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
        }
    }
}

// 推荐:直接在方法中处理
@Service
public class UserService {
    
    public User createUser(User user) {
        Assert.notNull(user, "用户信息不能为空");
        return userRepository.save(user);
    }
}

3. 缓存切点匹配结果

java
@Aspect
@Component
public class CachedPointcutAspect {
    
    private final Map<Method, Boolean> pointcutCache = new ConcurrentHashMap<>();
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object cachedPointcutExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        
        // 缓存切点匹配结果
        Boolean shouldApply = pointcutCache.computeIfAbsent(method, this::shouldApplyAdvice);
        
        if (shouldApply) {
            // 应用通知逻辑
            return applyAdvice(joinPoint);
        } else {
            return joinPoint.proceed();
        }
    }
    
    private Boolean shouldApplyAdvice(Method method) {
        // 复杂的匹配逻辑
        return method.isAnnotationPresent(Loggable.class);
    }
    
    private Object applyAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        // 通知逻辑
        return joinPoint.proceed();
    }
}

4. 异步处理

java
@Aspect
@Component
public class AsyncLoggingAspect {
    
    @Async
    @AfterReturning("execution(* com.example.service.*.*(..))") 
    public void logAsync(JoinPoint joinPoint) {
        // 异步记录日志,不影响主流程性能
        String methodName = joinPoint.getSignature().getName();
        logger.info("异步记录:方法 {} 执行完成", methodName);
    }
}

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-logging-");
        executor.initialize();
        return executor;
    }
}

最佳实践

1. 切面设计原则

java
// 单一职责:每个切面只处理一种横切关注点
@Aspect
@Component
public class LoggingAspect {
    // 只处理日志记录
}

@Aspect
@Component
public class SecurityAspect {
    // 只处理安全检查
}

@Aspect
@Component
public class PerformanceAspect {
    // 只处理性能监控
}

2. 切点表达式最佳实践

java
@Aspect
@Component
public class BestPracticeAspect {
    
    // 推荐:使用具体的包名
    @Pointcut("execution(* com.example.service.*.*(..))") 
    public void serviceLayer() {}
    
    // 推荐:使用注解切点
    @Pointcut("@annotation(Loggable)")
    public void loggableMethods() {}
    
    // 推荐:组合使用提高精确度
    @Pointcut("serviceLayer() && @annotation(Transactional)")
    public void transactionalServiceMethods() {}
    
    // 避免:过于宽泛的表达式
    // @Pointcut("execution(* *.*(..))") 
    
    // 避免:复杂的表达式
    // @Pointcut("execution(* com.example..*.*(..)) && !execution(* com.example.util..*.*(..)) && args(String, ..)")
}

3. 异常处理

java
@Aspect
@Component
public class RobustAspect {
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object robustExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // 前置处理
            preProcess(joinPoint);
            
            Object result = joinPoint.proceed();
            
            // 后置处理
            postProcess(joinPoint, result);
            
            return result;
        } catch (Exception e) {
            // 异常处理
            handleException(joinPoint, e);
            throw e;
        }
    }
    
    private void preProcess(JoinPoint joinPoint) {
        try {
            // 前置处理逻辑
        } catch (Exception e) {
            // 记录日志,但不影响主流程
            logger.warn("前置处理失败", e);
        }
    }
    
    private void postProcess(JoinPoint joinPoint, Object result) {
        try {
            // 后置处理逻辑
        } catch (Exception e) {
            // 记录日志,但不影响主流程
            logger.warn("后置处理失败", e);
        }
    }
    
    private void handleException(JoinPoint joinPoint, Exception e) {
        try {
            // 异常处理逻辑
        } catch (Exception ex) {
            // 记录日志,但不抛出新异常
            logger.error("异常处理失败", ex);
        }
    }
}

4. 测试友好的设计

java
@Aspect
@Component
public class TestableAspect {
    
    private final LoggingService loggingService;
    
    // 使用依赖注入,便于测试
    public TestableAspect(LoggingService loggingService) {
        this.loggingService = loggingService;
    }
    
    @Around("@annotation(Loggable)")
    public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        return loggingService.logMethodExecution(joinPoint);
    }
}

// 测试类
@ExtendWith(MockitoExtension.class)
class TestableAspectTest {
    
    @Mock
    private LoggingService loggingService;
    
    @InjectMocks
    private TestableAspect aspect;
    
    @Test
    void shouldLogMethodExecution() throws Throwable {
        // 测试切面逻辑
    }
}

5. 配置管理

java
@Aspect
@Component
@ConditionalOnProperty(name = "app.aop.enabled", havingValue = "true", matchIfMissing = true)
public class ConfigurableAspect {
    
    @Value("${app.aop.logging.enabled:true}")
    private boolean loggingEnabled;
    
    @Value("${app.aop.performance.enabled:false}")
    private boolean performanceEnabled;
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object conditionalExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        if (loggingEnabled) {
            logMethodExecution(joinPoint);
        }
        
        long startTime = performanceEnabled ? System.currentTimeMillis() : 0;
        
        try {
            Object result = joinPoint.proceed();
            
            if (performanceEnabled) {
                long endTime = System.currentTimeMillis();
                logger.info("方法执行时间:{}ms", endTime - startTime);
            }
            
            return result;
        } catch (Exception e) {
            if (loggingEnabled) {
                logger.error("方法执行异常", e);
            }
            throw e;
        }
    }
}

6. 切面顺序管理

java
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
public class SecurityAspect {
    
    @Before("execution(* com.example.service.*.*(..))") 
    public void checkSecurity() {
        // 安全检查应该最先执行
    }
}

@Aspect
@Component
@Order(1)
public class LoggingAspect {
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        // 日志记录
        return joinPoint.proceed();
    }
}

@Aspect
@Component
@Order(2)
public class PerformanceAspect {
    
    @Around("execution(* com.example.service.*.*(..))") 
    public Object measurePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        // 性能监控
        return joinPoint.proceed();
    }
}

@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE) // 最低优先级
public class CleanupAspect {
    
    @After("execution(* com.example.service.*.*(..))") 
    public void cleanup() {
        // 清理工作应该最后执行
    }
}

调试和故障排除

1. 启用 AOP 调试

properties
# application.properties
logging.level.org.springframework.aop=DEBUG
logging.level.org.aspectj=DEBUG

# 显示代理创建信息
spring.aop.proxy-target-class=true
spring.aop.auto=true

2. 检查代理创建

java
@Component
public class AopDebugger implements ApplicationListener<ContextRefreshedEvent> {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 检查哪些 Bean 被代理了
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        
        for (String beanName : beanNames) {
            Object bean = applicationContext.getBean(beanName);
            
            if (AopUtils.isAopProxy(bean)) {
                logger.info("Bean {} 被 AOP 代理", beanName);
                
                if (AopUtils.isJdkDynamicProxy(bean)) {
                    logger.info("  - 使用 JDK 动态代理");
                } else if (AopUtils.isCglibProxy(bean)) {
                    logger.info("  - 使用 CGLIB 代理");
                }
                
                // 显示切面信息
                if (bean instanceof Advised) {
                    Advised advised = (Advised) bean;
                    Advisor[] advisors = advised.getAdvisors();
                    
                    for (Advisor advisor : advisors) {
                        logger.info("  - 应用切面:{}", advisor.getClass().getSimpleName());
                    }
                }
            }
        }
    }
}

3. 常见问题解决

java
// 问题1:内部方法调用不触发 AOP
@Service
public class UserService {
    
    @Transactional
    public void publicMethod() {
        // 这个调用不会触发事务
        this.privateMethod();
    }
    
    @Transactional
    private void privateMethod() {
        // 事务不会生效
    }
}

// 解决方案:使用代理调用
@Service
public class UserService {
    
    @Autowired
    private UserService self;
    
    @Transactional
    public void publicMethod() {
        // 通过代理调用,事务会生效
        self.privateMethod();
    }
    
    @Transactional
    public void privateMethod() {
        // 现在事务会生效
    }
}

// 问题2:切点表达式不匹配
@Aspect
@Component
public class DebuggingAspect {
    
    @Before("execution(* com.example.service.*.*(..))") 
    public void debugPointcut(JoinPoint joinPoint) {
        logger.debug("切点匹配:{}", joinPoint.getSignature());
    }
    
    // 添加更宽泛的切点进行调试
    @Before("execution(* com.example..*.*(..))") 
    public void debugWidePointcut(JoinPoint joinPoint) {
        logger.debug("宽泛切点匹配:{}", joinPoint.getSignature());
    }
}

总结

Spring AOP 是一个强大的面向切面编程框架,它通过代理模式实现了横切关注点的模块化。主要特点包括:

核心优势

  1. 关注点分离:将横切关注点从业务逻辑中分离出来
  2. 代码复用:避免在多个地方重复相同的横切逻辑
  3. 松耦合:业务代码与横切关注点解耦
  4. 声明式编程:通过注解或配置声明切面行为
  5. 运行时织入:在运行时动态应用切面逻辑

适用场景

  1. 日志记录:统一的方法调用日志
  2. 性能监控:方法执行时间统计
  3. 安全控制:权限检查和身份验证
  4. 事务管理:声明式事务处理
  5. 缓存管理:自动缓存方法结果
  6. 异常处理:统一的异常处理逻辑

最佳实践总结

  1. 精确的切点表达式:避免过于宽泛的匹配
  2. 单一职责原则:每个切面只处理一种关注点
  3. 异常安全:确保切面逻辑不影响主业务流程
  4. 性能考虑:避免在高频调用的方法上使用复杂切面
  5. 测试友好:设计易于测试的切面
  6. 配置管理:支持动态开启/关闭切面功能

注意事项

  1. 代理限制:了解 JDK 动态代理和 CGLIB 代理的限制
  2. 内部调用:注意内部方法调用不会触发 AOP
  3. 性能影响:合理使用,避免过度使用影响性能
  4. 调试困难:AOP 可能会增加调试的复杂性
  5. 学习成本:需要理解 AOP 的概念和 AspectJ 表达式

Spring AOP 为企业级应用开发提供了强大的横切关注点处理能力,通过合理使用可以显著提高代码的可维护性和可重用性。在实际项目中,建议根据具体需求选择合适的切面策略,并遵循最佳实践来确保系统的稳定性和性能。