Spring AOP 实战指南:从入门到实际应用

253 阅读10分钟

Spring AOP 实战指南:从入门到实际应用

前言

AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的核心功能之一,它提供了一种优雅的方式来处理横切关注点。本文将深入探讨Spring AOP的核心概念,并通过多个实际应用场景来展示其强大的功能。

技术环境

  • JDK: 1.8
  • Spring Boot: 2.7.14
  • Spring AOP: 5.3.21(Spring Boot 2.7.14内置)

1. Spring AOP 基础概念

1.1 核心概念

  • 切面(Aspect): 横切关注点的模块化,如日志、事务等
  • 连接点(Join Point): 程序执行的某个特定位置,如方法调用
  • 切点(Pointcut): 匹配连接点的表达式
  • 通知(Advice): 在切点执行的代码
  • 目标对象(Target Object): 被代理的对象
  • AOP代理(AOP Proxy): 由AOP框架创建的对象

1.2 通知类型

  • @Before: 前置通知,在目标方法执行前执行
  • @After: 后置通知,在目标方法执行后执行
  • @AfterReturning: 返回通知,在目标方法正常返回后执行
  • @AfterThrowing: 异常通知,在目标方法抛出异常后执行
  • @Around: 环绕通知,包围目标方法执行

2. 环境配置

2.1 Maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.14</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <version>2.7.14</version>
    </dependency>
</dependencies>

2.2 启用AOP

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. 实际应用场景

3.1 日志记录

这是AOP最常见的应用场景之一,可以统一记录方法的执行信息。

@Aspect
@Component
@Slf4j
public class LoggingAspect {
    
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 获取方法信息
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        log.info("开始执行: {}.{}(), 参数: {}", className, methodName, Arrays.toString(args));
        
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            log.info("执行完成: {}.{}(), 耗时: {}ms, 结果: {}", 
                    className, methodName, endTime - startTime, result);
            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            log.error("执行异常: {}.{}(), 耗时: {}ms, 异常: {}", 
                    className, methodName, endTime - startTime, e.getMessage());
            throw e;
        }
    }
}

3.2 权限验证

通过AOP可以实现统一的权限控制,避免在每个方法中重复编写权限检查代码。

// 自定义权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
    String value() default "";
    String[] roles() default {};
}

// 权限校验切面
@Aspect
@Component
@Slf4j
public class PermissionAspect {
    
    @Autowired
    private UserService userService;
    
    @Before("@annotation(requirePermission)")
    public void checkPermission(JoinPoint joinPoint, RequirePermission requirePermission) {
        // 获取当前用户
        String currentUser = getCurrentUser();
        
        // 检查角色权限
        String[] requiredRoles = requirePermission.roles();
        if (requiredRoles.length > 0) {
            boolean hasRole = userService.hasAnyRole(currentUser, requiredRoles);
            if (!hasRole) {
                throw new PermissionDeniedException("权限不足,需要角色: " + Arrays.toString(requiredRoles));
            }
        }
        
        // 检查具体权限
        String permission = requirePermission.value();
        if (StringUtils.isNotBlank(permission)) {
            boolean hasPermission = userService.hasPermission(currentUser, permission);
            if (!hasPermission) {
                throw new PermissionDeniedException("权限不足,需要权限: " + permission);
            }
        }
        
        log.info("用户: {} 通过权限验证", currentUser);
    }
    
    private String getCurrentUser() {
        // 从SecurityContext或Session中获取当前用户
        return "currentUser";
    }
}

// 使用示例
@RestController
@RequestMapping("/admin")
public class AdminController {
    
    @RequirePermission(roles = {"ADMIN", "MANAGER"})
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.getAllUsers();
    }
    
    @RequirePermission(value = "user:delete", roles = {"ADMIN"})
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

3.3 缓存处理

实现方法级别的缓存,提高系统性能。

// 自定义缓存注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cacheable {
    String key() default "";
    int expireSeconds() default 3600;
    String condition() default "";
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheEvict {
    String key() default "";
    String[] keys() default {};
    boolean allEntries() default false;
}

// 缓存切面
@Aspect
@Component
@Slf4j
public class CacheAspect {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Around("@annotation(cacheable)")
    public Object handleCacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String key = generateKey(joinPoint, cacheable.key());
        
        // 检查条件
        if (StringUtils.isNotBlank(cacheable.condition()) && !evaluateCondition(cacheable.condition(), joinPoint)) {
            return joinPoint.proceed();
        }
        
        // 尝试从缓存获取
        Object cached = redisTemplate.opsForValue().get(key);
        if (cached != null) {
            log.info("缓存命中: {}", key);
            return cached;
        }
        
        // 执行方法并缓存结果
        Object result = joinPoint.proceed();
        if (result != null) {
            redisTemplate.opsForValue().set(key, result, cacheable.expireSeconds(), TimeUnit.SECONDS);
            log.info("缓存存储: {}, 过期时间: {}秒", key, cacheable.expireSeconds());
        }
        
        return result;
    }
    
    @After("@annotation(cacheEvict)")
    public void handleCacheEvict(JoinPoint joinPoint, CacheEvict cacheEvict) {
        if (cacheEvict.allEntries()) {
            // 清空所有缓存
            Set<String> keys = redisTemplate.keys("*");
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.info("清空所有缓存");
            }
        } else {
            // 清空指定缓存
            String[] keys = cacheEvict.keys();
            if (keys.length == 0) {
                String key = generateKey(joinPoint, cacheEvict.key());
                redisTemplate.delete(key);
                log.info("清空缓存: {}", key);
            } else {
                redisTemplate.delete(Arrays.asList(keys));
                log.info("清空缓存: {}", Arrays.toString(keys));
            }
        }
    }
    
    private String generateKey(JoinPoint joinPoint, String keyExpression) {
        if (StringUtils.isBlank(keyExpression)) {
            // 默认key生成策略
            return joinPoint.getSignature().toShortString() + ":" + Arrays.hashCode(joinPoint.getArgs());
        }
        // 可以实现SpEL表达式解析
        return keyExpression;
    }
    
    private boolean evaluateCondition(String condition, JoinPoint joinPoint) {
        // 简单条件评估,实际项目中可使用SpEL
        return true;
    }
}

// 使用示例
@Service
public class UserService {
    
    @Cacheable(key = "user:${id}", expireSeconds = 1800)
    public User getUserById(Long id) {
        // 模拟数据库查询
        return userRepository.findById(id);
    }
    
    @CacheEvict(key = "user:${user.id}")
    public void updateUser(User user) {
        userRepository.save(user);
    }
    
    @CacheEvict(allEntries = true)
    public void clearAllUserCache() {
        log.info("清空所有用户缓存");
    }
}

3.4 异常处理

统一处理业务异常,提供更好的错误信息。

@Aspect
@Component
@Slf4j
public class ExceptionHandlingAspect {
    
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void handleServiceException(JoinPoint joinPoint, Exception ex) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        // 记录异常日志
        log.error("服务异常 - 类: {}, 方法: {}, 参数: {}, 异常: {}", 
                className, methodName, Arrays.toString(args), ex.getMessage(), ex);
        
        // 发送异常通知
        sendExceptionNotification(className, methodName, ex);
        
        // 异常转换
        if (ex instanceof DataAccessException) {
            throw new ServiceException("数据访问异常", ex);
        } else if (ex instanceof IllegalArgumentException) {
            throw new ServiceException("参数错误: " + ex.getMessage(), ex);
        }
    }
    
    private void sendExceptionNotification(String className, String methodName, Exception ex) {
        // 发送邮件、短信或其他通知方式
        log.info("发送异常通知: {}.{} - {}", className, methodName, ex.getMessage());
    }
}

3.5 性能监控

监控方法执行时间,识别性能瓶颈。

@Aspect
@Component
@Slf4j
public class PerformanceMonitoringAspect {
    
    private final MeterRegistry meterRegistry;
    
    public PerformanceMonitoringAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Around("execution(* com.example.service.*.*(..)) || execution(* com.example.controller.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String metricName = className + "." + methodName;
        
        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));
            
            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.errors")
                    .tag("class", className)
                    .tag("method", methodName)
                    .tag("exception", e.getClass().getSimpleName())
                    .register(meterRegistry)
                    .increment();
            
            throw e;
        }
    }
    
    @AfterReturning("execution(* com.example.service.*.*(..))")
    public void recordMethodExecution(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        // 记录方法调用次数
        Counter.builder("method.execution.count")
                .tag("class", className)
                .tag("method", methodName)
                .register(meterRegistry)
                .increment();
    }
}

3.6 参数验证

实现统一的参数验证。

// 自定义验证注解
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateParam {
    String value() default "";
    boolean required() default true;
    int maxLength() default Integer.MAX_VALUE;
    int minLength() default 0;
    String pattern() default "";
}

@Aspect
@Component
@Slf4j
public class ValidationAspect {
    
    @Before("@annotation(validateParam) || execution(* *(.., @ValidateParam (*), ..))")
    public void validateParameters(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();
        
        for (int i = 0; i < parameters.length; i++) {
            ValidateParam validateParam = parameters[i].getAnnotation(ValidateParam.class);
            if (validateParam != null) {
                validateParameter(parameters[i].getName(), args[i], validateParam);
            }
        }
    }
    
    private void validateParameter(String paramName, Object value, ValidateParam validateParam) {
        // 必填验证
        if (validateParam.required() && value == null) {
            throw new IllegalArgumentException("参数 " + paramName + " 不能为空");
        }
        
        if (value != null && value instanceof String) {
            String strValue = (String) value;
            
            // 长度验证
            if (strValue.length() < validateParam.minLength()) {
                throw new IllegalArgumentException("参数 " + paramName + " 长度不能少于 " + validateParam.minLength());
            }
            
            if (strValue.length() > validateParam.maxLength()) {
                throw new IllegalArgumentException("参数 " + paramName + " 长度不能超过 " + validateParam.maxLength());
            }
            
            // 正则验证
            if (StringUtils.isNotBlank(validateParam.pattern()) && !strValue.matches(validateParam.pattern())) {
                throw new IllegalArgumentException("参数 " + paramName + " 格式不正确");
            }
        }
        
        log.debug("参数验证通过: {} = {}", paramName, value);
    }
}

// 使用示例
@RestController
@RequestMapping("/api")
public class ApiController {
    
    @PostMapping("/users")
    public User createUser(@ValidateParam(minLength = 2, maxLength = 50) String name,
                          @ValidateParam(pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") String email) {
        return userService.createUser(name, email);
    }
}

3.7 审计日志

记录敏感操作的审计信息。

// 审计注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Audit {
    String operation() default "";
    String module() default "";
    boolean logParams() default true;
    boolean logResult() default false;
}

@Aspect
@Component
@Slf4j
public class AuditAspect {
    
    @Autowired
    private AuditLogService auditLogService;
    
    @Around("@annotation(audit)")
    public Object auditOperation(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
        String currentUser = getCurrentUser();
        String operation = StringUtils.isNotBlank(audit.operation()) ? audit.operation() : joinPoint.getSignature().getName();
        String module = audit.module();
        
        AuditLog auditLog = AuditLog.builder()
                .userId(currentUser)
                .operation(operation)
                .module(module)
                .className(joinPoint.getTarget().getClass().getSimpleName())
                .methodName(joinPoint.getSignature().getName())
                .timestamp(LocalDateTime.now())
                .build();
        
        if (audit.logParams()) {
            auditLog.setParameters(JSON.toJSONString(joinPoint.getArgs()));
        }
        
        try {
            Object result = joinPoint.proceed();
            
            auditLog.setStatus("SUCCESS");
            if (audit.logResult()) {
                auditLog.setResult(JSON.toJSONString(result));
            }
            
            log.info("审计日志 - 用户: {}, 操作: {}, 模块: {}, 状态: SUCCESS", currentUser, operation, module);
            return result;
        } catch (Exception e) {
            auditLog.setStatus("FAILURE");
            auditLog.setErrorMessage(e.getMessage());
            
            log.error("审计日志 - 用户: {}, 操作: {}, 模块: {}, 状态: FAILURE, 错误: {}", 
                    currentUser, operation, module, e.getMessage());
            throw e;
        } finally {
            auditLogService.save(auditLog);
        }
    }
    
    private String getCurrentUser() {
        // 从SecurityContext获取当前用户
        return "currentUser";
    }
}

// 使用示例
@Service
public class UserService {
    
    @Audit(operation = "删除用户", module = "用户管理", logParams = true)
    public void deleteUser(Long userId) {
        userRepository.deleteById(userId);
    }
    
    @Audit(operation = "更新用户权限", module = "权限管理", logParams = true, logResult = true)
    public User updateUserPermissions(Long userId, List<String> permissions) {
        User user = userRepository.findById(userId);
        user.setPermissions(permissions);
        return userRepository.save(user);
    }
}

3.8 限流控制

实现方法级别的限流。

// 限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    int count() default 10;
    int period() default 60;
    String key() default "";
    String message() default "请求过于频繁,请稍后再试";
}

@Aspect
@Component
@Slf4j
public class RateLimitAspect {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Before("@annotation(rateLimit)")
    public void checkRateLimit(JoinPoint joinPoint, RateLimit rateLimit) {
        String key = generateKey(joinPoint, rateLimit.key());
        int count = rateLimit.count();
        int period = rateLimit.period();
        
        // 获取当前计数
        String countKey = "rate_limit:" + key;
        Integer currentCount = (Integer) redisTemplate.opsForValue().get(countKey);
        
        if (currentCount == null) {
            // 第一次请求
            redisTemplate.opsForValue().set(countKey, 1, period, TimeUnit.SECONDS);
            log.debug("限流检查 - 第一次请求: {}", key);
        } else if (currentCount < count) {
            // 未达到限制
            redisTemplate.opsForValue().increment(countKey);
            log.debug("限流检查 - 当前请求数: {}/{}, key: {}", currentCount + 1, count, key);
        } else {
            // 达到限制
            log.warn("限流触发 - 请求数: {}/{}, key: {}", currentCount, count, key);
            throw new RateLimitExceededException(rateLimit.message());
        }
    }
    
    private String generateKey(JoinPoint joinPoint, String keyExpression) {
        if (StringUtils.isNotBlank(keyExpression)) {
            return keyExpression;
        }
        
        // 默认key生成策略:IP + 类名 + 方法名
        String ip = getClientIp();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        return ip + ":" + className + ":" + methodName;
    }
    
    private String getClientIp() {
        // 获取客户端IP
        return "127.0.0.1";
    }
}

// 使用示例
@RestController
@RequestMapping("/api")
public class ApiController {
    
    @RateLimit(count = 5, period = 60, message = "获取用户信息过于频繁")
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @RateLimit(count = 3, period = 300, key = "send_sms:${phone}")
    @PostMapping("/send-sms")
    public void sendSms(@RequestParam String phone) {
        smsService.sendSms(phone);
    }
}

3.9 数据脱敏

对敏感数据进行脱敏处理。

// 脱敏注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMask {
    String[] fields() default {};
    MaskType type() default MaskType.CUSTOM;
}

public enum MaskType {
    PHONE,      // 手机号脱敏
    EMAIL,      // 邮箱脱敏
    ID_CARD,    // 身份证脱敏
    BANK_CARD,  // 银行卡脱敏
    CUSTOM      // 自定义脱敏
}

@Aspect
@Component
@Slf4j
public class DataMaskAspect {
    
    @AfterReturning(value = "@annotation(dataMask)", returning = "result")
    public Object maskSensitiveData(JoinPoint joinPoint, DataMask dataMask, Object result) {
        if (result == null) {
            return result;
        }
        
        try {
            // 深拷贝对象以避免修改原对象
            Object maskedResult = deepCopy(result);
            
            // 执行脱敏
            maskFields(maskedResult, dataMask.fields(), dataMask.type());
            
            log.debug("数据脱敏完成: {}", joinPoint.getSignature().getName());
            return maskedResult;
        } catch (Exception e) {
            log.error("数据脱敏失败: {}", e.getMessage(), e);
            return result;
        }
    }
    
    private void maskFields(Object obj, String[] fields, MaskType type) throws Exception {
        if (obj == null) return;
        
        Class<?> clazz = obj.getClass();
        
        // 处理集合类型
        if (obj instanceof Collection) {
            Collection<?> collection = (Collection<?>) obj;
            for (Object item : collection) {
                maskFields(item, fields, type);
            }
            return;
        }
        
        // 处理普通对象
        for (String fieldName : fields) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                Object value = field.get(obj);
                
                if (value instanceof String) {
                    String maskedValue = maskString((String) value, type);
                    field.set(obj, maskedValue);
                }
            } catch (NoSuchFieldException e) {
                log.warn("字段不存在: {}", fieldName);
            }
        }
    }
    
    private String maskString(String value, MaskType type) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        
        switch (type) {
            case PHONE:
                return maskPhone(value);
            case EMAIL:
                return maskEmail(value);
            case ID_CARD:
                return maskIdCard(value);
            case BANK_CARD:
                return maskBankCard(value);
            default:
                return maskDefault(value);
        }
    }
    
    private String maskPhone(String phone) {
        if (phone.length() >= 11) {
            return phone.substring(0, 3) + "****" + phone.substring(7);
        }
        return phone;
    }
    
    private String maskEmail(String email) {
        int atIndex = email.indexOf('@');
        if (atIndex > 0) {
            String username = email.substring(0, atIndex);
            String domain = email.substring(atIndex);
            String maskedUsername = username.length() > 2 ? 
                username.substring(0, 2) + "****" : username;
            return maskedUsername + domain;
        }
        return email;
    }
    
    private String maskIdCard(String idCard) {
        if (idCard.length() >= 18) {
            return idCard.substring(0, 6) + "********" + idCard.substring(14);
        }
        return idCard;
    }
    
    private String maskBankCard(String bankCard) {
        if (bankCard.length() >= 16) {
            return bankCard.substring(0, 4) + "********" + bankCard.substring(bankCard.length() - 4);
        }
        return bankCard;
    }
    
    private String maskDefault(String value) {
        if (value.length() <= 2) {
            return "*".repeat(value.length());
        }
        return value.substring(0, 1) + "*".repeat(value.length() - 2) + value.substring(value.length() - 1);
    }
    
    private Object deepCopy(Object obj) {
        // 使用JSON序列化进行深拷贝
        return JSON.parseObject(JSON.toJSONString(obj), obj.getClass());
    }
}

// 使用示例
@RestController
@RequestMapping("/api")
public class UserController {
    
    @DataMask(fields = {"phone", "email"}, type = MaskType.PHONE)
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @DataMask(fields = {"phone"}, type = MaskType.PHONE)
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.getAllUsers();
    }
}

3.10 重试机制

实现方法级别的重试机制。

// 重试注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retry {
    int times() default 3;
    long delay() default 1000;
    Class<? extends Exception>[] retryFor() default {Exception.class};
    Class<? extends Exception>[] noRetryFor() default {};
}

@Aspect
@Component
@Slf4j
public class RetryAspect {
    
    @Around("@annotation(retry)")
    public Object retryOperation(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
        int maxAttempts = retry.times();
        long delay = retry.delay();
        Class<? extends Exception>[] retryFor = retry.retryFor();
        Class<? extends Exception>[] noRetryFor = retry.noRetryFor();
        
        String methodName = joinPoint.getSignature().getName();
        
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                Object result = joinPoint.proceed();
                if (attempt > 1) {
                    log.info("方法 {} 在第 {} 次尝试后成功执行", methodName, attempt);
                }
                return result;
            } catch (Exception e) {
                // 检查是否需要重试
                if (!shouldRetry(e, retryFor, noRetryFor)) {
                    log.error("方法 {} 抛出不可重试异常: {}", methodName, e.getMessage());
                    throw e;
                }
                
                if (attempt == maxAttempts) {
                    log.error("方法 {} 在 {} 次尝试后仍然失败: {}", methodName, maxAttempts, e.getMessage());
                    throw e;
                }
                
                log.warn("方法 {} 第 {} 次尝试失败,{}ms后重试: {}", methodName, attempt, delay, e.getMessage());
                
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("重试被中断", ie);
                }
            }
        }
        
        throw new RuntimeException("重试机制异常");
    }
    
    private boolean shouldRetry(Exception e, Class<? extends Exception>[] retryFor, Class<? extends Exception>[] noRetryFor) {
        // 检查不重试的异常
        for (Class<? extends Exception> noRetryException : noRetryFor) {
            if (noRetryException.isAssignableFrom(e.getClass())) {
                return false;
            }
        }
        
        // 检查重试的异常
        for (Class<? extends Exception> retryException : retryFor) {
            if (retryException.isAssignableFrom(e.getClass())) {
                return true;
            }
        }
        
        return false;
    }
}

// 使用示例
@Service
public class ExternalService {
    
    @Retry(times = 3, delay = 2000, retryFor = {ConnectException.class, SocketTimeoutException.class})
    public String callExternalApi(String url) {
        // 模拟外部API调用
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(url, String.class);
    }
    
    @Retry(times = 5, delay = 1000, noRetryFor = {IllegalArgumentException.class})
    public void processData(String data) {
        if (StringUtils.isBlank(data)) {
            throw new IllegalArgumentException("数据不能为空");
        }
        
        // 模拟可能失败的数据处理
        if (Math.random() < 0.3) {
            throw new RuntimeException("数据处理失败");
        }
    }
}

4. 最佳实践

4.1 切点表达式优化

@Component
@Aspect
public class OptimizedAspect {
    
    // 使用命名切点,提高可读性和复用性
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    @Pointcut("execution(* com.example.controller.*.*(..))")
    public void controllerLayer() {}
    
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethod() {}
    
    // 组合切点
    @Pointcut("serviceLayer() || controllerLayer()")
    public void applicationLayer() {}
    
    // 排除某些方法
    @Pointcut("applicationLayer() && !execution(* com.example.service.LogService.*(..))")
    public void nonLoggingService() {}
}

4.2 性能考虑

@Aspect
@Component
public class PerformanceOptimizedAspect {
    
    // 使用条件判断避免不必要的处理
    @Around("@annotation(com.example.annotation.Monitor)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        // 只在开发环境或特定条件下执行监控
        if (!shouldMonitor()) {
            return joinPoint.proceed();
        }
        
        // 执行监控逻辑
        return doMonitoring(joinPoint);
    }
    
    private boolean shouldMonitor() {
        // 检查环境变量或配置
        return "dev".equals(System.getProperty("spring.profiles.active"));
    }
    
    private Object doMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {
        // 监控实现
        return joinPoint.proceed();
    }
}

4.3 异常处理

@Aspect
@Component
@Slf4j
public class RobustAspect {
    
    @Around("@annotation(com.example.annotation.SafeExecute)")
    public Object safeExecute(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            // 记录异常但不影响主流程
            log.error("切面执行异常: {}", e.getMessage(), e);
            
            // 根据需要决定是否重新抛出异常
            if (e instanceof RuntimeException) {
                throw e;
            }
            
            // 返回默认值或继续执行
            return getDefaultResult(joinPoint);
        }
    }
    
    private Object getDefaultResult(ProceedingJoinPoint joinPoint) {
        // 根据返回类型返回默认值
        Class<?> returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
        
        if (returnType == void.class) {
            return null;
        } else if (returnType == boolean.class) {
            return false;
        } else if (returnType.isPrimitive()) {
            return 0;
        } else if (Collection.class.isAssignableFrom(returnType)) {
            return Collections.emptyList();
        }
        
        return null;
    }
}

5. 常见问题和解决方案

5.1 AOP不生效

问题: AOP切面没有被执行

解决方案:

  1. 确保添加了 @EnableAspectJAutoProxy 注解
  2. 检查切点表达式是否正确
  3. 确保目标类被Spring管理(添加@Component等注解)
  4. 避免在同一个类中的方法调用(self-invocation问题)

5.2 性能问题

问题: AOP导致性能下降

解决方案:

  1. 优化切点表达式,避免过宽的匹配
  2. 在切面中避免复杂的逻辑
  3. 使用条件判断避免不必要的处理
  4. 考虑使用异步处理非关键功能

5.3 代理问题

问题: 内部方法调用时AOP不生效

解决方案:

@Service
public class UserService {
    
    @Autowired
    private UserService self;  // 注入自己
    
    public void publicMethod() {
        // 通过代理调用内部方法
        self.internalMethod();
    }
    
    @Transactional
    public void internalMethod() {
        // 事务方法
    }
}

6. 总结

Spring AOP是一个强大的编程范式,它能够帮助我们优雅地处理横切关注点,提高代码的可维护性和可读性。通过本文介绍的多个实际应用场景,我们可以看到AOP在日志记录、权限控制、缓存处理、异常处理等方面的强大能力。

在实际使用中,需要注意以下几点:

  1. 合理使用: 不要滥用AOP,只在确实需要横切关注点时使用
  2. 性能考虑: 注意AOP可能带来的性能影响
  3. 调试困难: AOP会增加调试的复杂性,需要良好的日志记录
  4. 团队协作: 确保团队成员都理解AOP的工作原理

通过合理使用Spring AOP,我们可以构建出更加清晰、可维护的应用程序。