15-Spring-Framework-AOP 原理详解

2 阅读12分钟

Spring AOP 原理详解

一、知识概述

AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的核心功能之一,它通过将横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,实现了关注点分离,提高了代码的模块化和可维护性。

AOP 主要应用场景:

  • 日志记录:统一的方法调用日志
  • 事务管理:声明式事务控制
  • 权限校验:方法级别的权限控制
  • 性能监控:方法执行时间统计
  • 异常处理:统一的异常处理

理解 AOP 的原理,有助于更好地使用 Spring 的事务管理、缓存等高级特性。

二、知识点详细讲解

2.1 AOP 核心概念

切面(Aspect)

切面是横切关注点的模块化封装,包含通知和切点。

@Aspect
@Component
public class LoggingAspect {
    // 切面 = 切点 + 通知
}
切点(Pointcut)

切点定义了通知在哪些连接点执行,使用切点表达式描述。

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

通知定义了切面在特定连接点执行的动作:

  • @Before:方法执行前
  • @AfterReturning:方法成功返回后
  • @AfterThrowing:方法抛出异常后
  • @After:方法执行后(无论成功或异常)
  • @Around:环绕方法执行(最强大)
连接点(Join Point)

连接点是程序执行的特定点,Spring AOP 中指方法执行。

目标对象(Target Object)

被通知的对象,即业务逻辑所在的类。

代理对象(Proxy)

AOP 框架创建的对象,包含通知逻辑。

织入(Weaving)

将切面应用到目标对象并创建代理对象的过程:

  • 编译期织入:AspectJ 编译器
  • 类加载期织入:AspectJ LTW
  • 运行期织入:Spring AOP(动态代理)

2.2 切点表达式

execution 表达式
execution(修饰符模式? 返回类型模式 方法名模式(参数模式) 异常模式?)

示例:
execution(* com.example.service.*.*(..))
execution(public * *(..))
execution(* set*(..))
execution(* com.example.service.UserService+.*(..))
其他切点指示符
  • within:匹配特定类型内的所有连接点
  • this:匹配代理对象类型
  • target:匹配目标对象类型
  • args:匹配参数类型
  • @annotation:匹配特定注解的方法
  • @within:匹配特定注解的类
  • @target:匹配特定注解的目标对象
  • @args:匹配特定注解的参数
组合表达式
@Pointcut("execution(* com.example.service.*.*(..)) && args(id,..)")
public void serviceMethodWithId(Long id) {}

@Pointcut("execution(* com.example.service.*.*(..)) || execution(* com.example.dao.*.*(..))")
public void serviceOrDao() {}

@Pointcut("execution(* com.example.service.*.*(..)) && !@annotation(NoLog)")
public void serviceWithoutNoLog() {}

2.3 代理机制

JDK 动态代理
  • 基于接口的代理
  • 使用 java.lang.reflect.Proxy
  • 只能代理接口方法
Object proxy = Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    invocationHandler
);
CGLIB 代理
  • 基于继承的代理
  • 使用字节码生成技术
  • 可以代理类(非 final)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(methodInterceptor);
Object proxy = enhancer.create();
Spring 选择规则
  1. 目标对象实现了接口 → 默认使用 JDK 动态代理
  2. 目标对象没有实现接口 → 使用 CGLIB
  3. 强制使用 CGLIB:@EnableAspectJAutoProxy(proxyTargetClass = true)

2.4 通知执行顺序

同一切面内的顺序
Around 前 → Before → 方法执行 → AfterReturning/AfterThrowing → After → Around 后
多切面的顺序

使用 @Order 或实现 Ordered 接口:

  • Order 值越小,优先级越高
  • Before 通知:Order 小的先执行
  • After 通知:Order 大的先执行

2.5 AspectJ 注解

注解说明
@Aspect声明切面
@Pointcut声明切点
@Before前置通知
@AfterReturning返回后通知
@AfterThrowing异常通知
@After后置通知
@Around环绕通知
@DeclareParents引入新功能

三、代码示例

3.1 基本切面示例

import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;

// 切面定义
@Aspect
@Component
public class LoggingAspect {
    
    // 切点定义:匹配 Service 层所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    // 切点定义:匹配特定注解
    @Pointcut("@annotation(com.example.annotation.Loggable)")
    public void loggableMethod() {}
    
    // 前置通知
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("执行方法: " + joinPoint.getSignature().getName());
        System.out.println("参数: " + java.util.Arrays.toString(joinPoint.getArgs()));
    }
    
    // 返回后通知
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("方法返回: " + joinPoint.getSignature().getName());
        System.out.println("返回值: " + result);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("方法异常: " + joinPoint.getSignature().getName());
        System.out.println("异常: " + error.getMessage());
    }
    
    // 后置通知
    @After("serviceLayer()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("方法完成: " + joinPoint.getSignature().getName());
    }
}

// 业务类
@Service
public class UserService {
    
    public User findById(Long id) {
        System.out.println("UserService.findById 执行");
        return new User(id, "User-" + id);
    }
    
    public void save(User user) {
        System.out.println("UserService.save 执行");
        if (user.getId() == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
    }
}

// 辅助类
public class User {
    private Long id;
    private String name;
    
    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "'}";
    }
}

3.2 环绕通知示例

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AroundAspect {
    
    // 环绕通知:最强大的通知类型
    @Around("execution(* com.example.service.*.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        System.out.println("=== 方法开始: " + methodName + " ===");
        System.out.println("参数: " + java.util.Arrays.toString(args));
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            long endTime = System.currentTimeMillis();
            System.out.println("执行时间: " + (endTime - startTime) + " ms");
            System.out.println("返回值: " + result);
            System.out.println("=== 方法结束: " + methodName + " ===\n");
            
            return result;
            
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            System.out.println("执行时间: " + (endTime - startTime) + " ms");
            System.out.println("异常: " + e.getClass().getSimpleName() + " - " + e.getMessage());
            System.out.println("=== 方法异常结束: " + methodName + " ===\n");
            
            // 可以选择重新抛出、返回默认值、或包装异常
            throw e;
        }
    }
}

// 性能监控切面
@Aspect
@Component
public class PerformanceAspect {
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.nanoTime();
        
        try {
            return joinPoint.proceed();
        } finally {
            long duration = System.nanoTime() - start;
            double ms = duration / 1_000_000.0;
            
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String methodName = joinPoint.getSignature().getName();
            
            System.out.println(String.format("[%s.%s] 耗时: %.2f ms", className, methodName, ms));
        }
    }
}

3.3 注解驱动的切面

import java.lang.annotation.*;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Loggable {
    String value() default "";
    LogLevel level() default LogLevel.INFO;
}

public enum LogLevel {
    DEBUG, INFO, WARN, ERROR
}

// 使用注解的业务方法
@Service
public class OrderService {
    
    @Loggable(value = "创建订单", level = LogLevel.INFO)
    public Order createOrder(Long userId, String productCode, int quantity) {
        System.out.println("创建订单: userId=" + userId + ", product=" + productCode);
        return new Order(System.currentTimeMillis(), userId, productCode, quantity);
    }
    
    @Loggable("取消订单")
    public void cancelOrder(Long orderId) {
        System.out.println("取消订单: " + orderId);
    }
}

// 日志切面
@Aspect
@Component
public class LoggableAspect {
    
    @Around("@annotation(loggable)")
    public Object log(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
        String operation = loggable.value();
        String methodName = joinPoint.getSignature().getName();
        
        System.out.println("[" + loggable.level() + "] 开始 " + operation);
        System.out.println("  方法: " + methodName);
        System.out.println("  参数: " + java.util.Arrays.toString(joinPoint.getArgs()));
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("[" + loggable.level() + "] 完成 " + operation);
            System.out.println("  耗时: " + duration + " ms");
            System.out.println("  结果: " + result);
            
            return result;
            
        } catch (Throwable e) {
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("[ERROR] 失败 " + operation);
            System.out.println("  耗时: " + duration + " ms");
            System.out.println("  异常: " + e.getMessage());
            
            throw e;
        }
    }
}

// 订单类
public class Order {
    private Long id;
    private Long userId;
    private String productCode;
    private int quantity;
    
    public Order(Long id, Long userId, String productCode, int quantity) {
        this.id = id;
        this.userId = userId;
        this.productCode = productCode;
        this.quantity = quantity;
    }
    
    @Override
    public String toString() {
        return "Order{id=" + id + ", userId=" + userId + ", productCode='" + productCode + "'}";
    }
}

3.4 多切面协作示例

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

// 切面1:日志
@Aspect
@Component
@Order(1)  // 优先级最高
public class LoggingAspect2 {
    
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("[1-Logging] 开始记录日志");
    }
    
    @After("execution(* com.example.service.*.*(..))")
    public void logAfter() {
        System.out.println("[1-Logging] 结束记录日志");
    }
}

// 切面2:事务
@Aspect
@Component
@Order(2)
public class TransactionAspect {
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[2-Transaction] 开启事务");
        try {
            Object result = joinPoint.proceed();
            System.out.println("[2-Transaction] 提交事务");
            return result;
        } catch (Exception e) {
            System.out.println("[2-Transaction] 回滚事务");
            throw e;
        }
    }
}

// 切面3:权限
@Aspect
@Component
@Order(3)
public class SecurityAspect {
    
    @Before("execution(* com.example.service.*.*(..))")
    public void checkPermission() {
        System.out.println("[3-Security] 检查权限");
    }
    
    @AfterReturning("execution(* com.example.service.*.*(..))")
    public void logSuccess() {
        System.out.println("[3-Security] 权限验证通过");
    }
}

// 执行顺序示例:
// [1-Logging] 开始记录日志
// [2-Transaction] 开启事务
// [3-Security] 检查权限
// 方法执行...
// [3-Security] 权限验证通过
// [2-Transaction] 提交事务
// [1-Logging] 结束记录日志

3.5 切点表达式详解

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PointcutExpressionDemo {
    
    // ==================== execution 表达式 ====================
    
    // 匹配所有 public 方法
    @Pointcut("execution(public * *(..))")
    public void anyPublicMethod() {}
    
    // 匹配 set 开头的方法
    @Pointcut("execution(* set*(..))")
    public void anySetMethod() {}
    
    // 匹配特定包下的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void servicePackage() {}
    
    // 匹配特定类的所有方法
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void userServiceMethods() {}
    
    // 匹配特定类及其子类的所有方法
    @Pointcut("execution(* com.example.service.UserService+.*(..))")
    public void userServiceAndSubclassMethods() {}
    
    // 匹配特定参数类型的方法
    @Pointcut("execution(* *(String, ..))")
    public void firstParamString() {}
    
    // 匹配无参方法
    @Pointcut("execution(* *())")
    public void noParamMethod() {}
    
    // 匹配抛出特定异常的方法
    @Pointcut("execution(* *.*(..) throws IOException)")
    public void throwsIOException() {}
    
    // ==================== within 表达式 ====================
    
    // 匹配特定包下的所有方法
    @Pointcut("within(com.example.service..*)")
    public void withinServicePackage() {}
    
    // 匹配特定类的所有方法
    @Pointcut("within(com.example.service.UserService)")
    public void withinUserService() {}
    
    // ==================== @annotation 表达式 ====================
    
    // 匹配带有特定注解的方法
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethod() {}
    
    // 匹配带有自定义注解的方法
    @Pointcut("@annotation(com.example.annotation.Cacheable)")
    public void cacheableMethod() {}
    
    // ==================== @within 表达式 ====================
    
    // 匹配带有特定注解的类的所有方法
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void withinServiceAnnotatedClass() {}
    
    // ==================== args 表达式 ====================
    
    // 匹配第一个参数为 Long 的方法
    @Pointcut("args(Long, ..)")
    public void firstArgLong() {}
    
    // 匹配参数类型组合
    @Pointcut("args(Long, String)")
    public void argsLongString() {}
    
    // ==================== 组合表达式 ====================
    
    // && 与运算
    @Pointcut("execution(* com.example.service.*.*(..)) && args(id,..)")
    public void serviceMethodWithFirstArg(Object id) {}
    
    // || 或运算
    @Pointcut("execution(* com.example.service.*.*(..)) || execution(* com.example.dao.*.*(..))")
    public void serviceOrDao() {}
    
    // ! 非运算
    @Pointcut("execution(* com.example.service.*.*(..)) && !execution(* com.example.service.*.get*(..))")
    public void serviceNonGetter() {}
    
    // ==================== 引用切点 ====================
    
    @Before("servicePackage() && args(id)")
    public void beforeServiceMethod(Object id) {
        System.out.println("Service 方法执行,第一个参数: " + id);
    }
    
    @AfterReturning(pointcut = "anyPublicMethod()", returning = "result")
    public void afterPublicMethod(Object result) {
        System.out.println("Public 方法返回: " + result);
    }
}

3.6 引入(Introduction)示例

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

// 新增的接口
public interface Auditable {
    void setLastModifiedBy(String user);
    String getLastModifiedBy();
}

// 接口实现
public class AuditableImpl implements Auditable {
    private String lastModifiedBy;
    
    @Override
    public void setLastModifiedBy(String user) {
        this.lastModifiedBy = user;
    }
    
    @Override
    public String getLastModifiedBy() {
        return lastModifiedBy;
    }
}

// 切面:为现有类引入新接口
@Aspect
@Component
public class IntroductionAspect {
    
    // 为所有 Service 类引入 Auditable 接口
    @DeclareParents(
        value = "com.example.service.*+", 
        defaultImpl = AuditableImpl.class
    )
    public static Auditable auditable;
}

// 使用示例
@Service
public class ProductService {
    // 原有类不需要修改
}

public class IntroductionDemo {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        
        ProductService productService = context.getBean(ProductService.class);
        
        // Product 原本没有实现 Auditable,但通过 @DeclareParents 引入了
        Auditable auditable = (Auditable) productService;
        auditable.setLastModifiedBy("admin");
        System.out.println("最后修改人: " + auditable.getLastModifiedBy());
    }
}

四、实战应用场景

4.1 统一异常处理

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.util.*;

@Aspect
@Component
public class ExceptionHandlingAspect {
    
    // 异常处理器映射
    private final Map<Class<? extends Throwable>, ExceptionHandler> handlers = new HashMap<>();
    
    public ExceptionHandlingAspect() {
        handlers.put(IllegalArgumentException.class, this::handleIllegalArgument);
        handlers.put(NullPointerException.class, this::handleNullPointer);
    }
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            return handleExceptionInternal(e, joinPoint);
        }
    }
    
    private Object handleExceptionInternal(Throwable e, ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        System.out.println("异常发生在方法: " + methodName);
        System.out.println("参数: " + Arrays.toString(args));
        System.out.println("异常类型: " + e.getClass().getSimpleName());
        System.out.println("异常信息: " + e.getMessage());
        
        // 查找对应的异常处理器
        ExceptionHandler handler = handlers.get(e.getClass());
        if (handler != null) {
            return handler.handle(e, joinPoint);
        }
        
        // 默认处理
        return createErrorResponse("系统异常", 500);
    }
    
    private Object handleIllegalArgument(Throwable e, ProceedingJoinPoint joinPoint) {
        return createErrorResponse("参数错误: " + e.getMessage(), 400);
    }
    
    private Object handleNullPointer(Throwable e, ProceedingJoinPoint joinPoint) {
        return createErrorResponse("空指针异常", 500);
    }
    
    private Response createErrorResponse(String message, int code) {
        return new Response(code, message, null);
    }
    
    @FunctionalInterface
    interface ExceptionHandler {
        Object handle(Throwable e, ProceedingJoinPoint joinPoint);
    }
}

// 响应类
public class Response {
    private int code;
    private String message;
    private Object data;
    
    public Response(int code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    
    @Override
    public String toString() {
        return "Response{code=" + code + ", message='" + message + "'}";
    }
}

4.2 方法缓存

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.annotation.*;

// 缓存注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheResult {
    String key() default "";
    int expire() default 300;  // 秒
}

// 简单缓存实现
@Component
public class SimpleCache {
    private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
    
    public Object get(String key) {
        CacheEntry entry = cache.get(key);
        if (entry != null && !entry.isExpired()) {
            return entry.value;
        }
        return null;
    }
    
    public void put(String key, Object value, int expireSeconds) {
        cache.put(key, new CacheEntry(value, System.currentTimeMillis() + expireSeconds * 1000));
    }
    
    public void clear() {
        cache.clear();
    }
    
    private static class CacheEntry {
        Object value;
        long expireTime;
        
        CacheEntry(Object value, long expireTime) {
            this.value = value;
            this.expireTime = expireTime;
        }
        
        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }
}

// 缓存切面
@Aspect
@Component
public class CacheAspect {
    
    private final SimpleCache cache;
    
    public CacheAspect(SimpleCache cache) {
        this.cache = cache;
    }
    
    @Around("@annotation(cacheResult)")
    public Object cache(ProceedingJoinPoint joinPoint, CacheResult cacheResult) throws Throwable {
        // 生成缓存键
        String key = generateKey(joinPoint, cacheResult.key());
        
        // 尝试从缓存获取
        Object cachedValue = cache.get(key);
        if (cachedValue != null) {
            System.out.println("缓存命中: " + key);
            return cachedValue;
        }
        
        // 执行方法
        System.out.println("缓存未命中,执行方法: " + key);
        Object result = joinPoint.proceed();
        
        // 存入缓存
        if (result != null) {
            cache.put(key, result, cacheResult.expire());
        }
        
        return result;
    }
    
    private String generateKey(ProceedingJoinPoint joinPoint, String keyPrefix) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        
        return keyPrefix.isEmpty() 
            ? className + "." + methodName + ":" + args
            : keyPrefix + ":" + args;
    }
}

// 使用示例
@Service
public class DataService {
    
    @CacheResult(key = "user", expire = 60)
    public User getUserById(Long id) {
        System.out.println("查询数据库: SELECT * FROM user WHERE id = " + id);
        return new User(id, "User-" + id);
    }
    
    @CacheResult
    public List<User> getAllUsers() {
        System.out.println("查询数据库: SELECT * FROM user");
        return Arrays.asList(
            new User(1L, "User-1"),
            new User(2L, "User-2")
        );
    }
}

4.3 限流切面

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

// 限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int value() default 10;  // 每秒请求数
    int timeout() default 1000;  // 等待超时时间(ms)
}

// 限流器
@Component
public class RateLimiter {
    
    private final Map<String, Semaphore> limiters = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int permits, int timeout) {
        Semaphore semaphore = limiters.computeIfAbsent(key, k -> new Semaphore(permits));
        try {
            return semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
    
    public void release(String key) {
        Semaphore semaphore = limiters.get(key);
        if (semaphore != null) {
            semaphore.release();
        }
    }
}

// 限流切面
@Aspect
@Component
public class RateLimitAspect {
    
    private final RateLimiter rateLimiter;
    private final AtomicLong requestCount = new AtomicLong(0);
    
    public RateLimitAspect(RateLimiter rateLimiter) {
        this.rateLimiter = rateLimiter;
    }
    
    @Around("@annotation(rateLimit)")
    public Object limit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String key = joinPoint.getSignature().toLongString();
        
        long count = requestCount.incrementAndGet();
        System.out.println("请求计数: " + count);
        
        if (!rateLimiter.tryAcquire(key, 1, rateLimit.timeout())) {
            System.out.println("限流触发: " + key);
            throw new RuntimeException("请求过于频繁,请稍后再试");
        }
        
        try {
            return joinPoint.proceed();
        } finally {
            rateLimiter.release(key);
        }
    }
}

// 使用示例
@Service
public class ApiClient {
    
    @RateLimit(value = 5, timeout = 500)  // 每秒最多5次
    public String callExternalApi(String endpoint) {
        System.out.println("调用外部API: " + endpoint);
        return "Response from " + endpoint;
    }
}

4.4 审计日志

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
import java.time.LocalDateTime;

// 审计注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
    String action();
    String description() default "";
}

// 审计日志
public class AuditLog {
    private String action;
    private String description;
    private String className;
    private String methodName;
    private Object[] args;
    private Object result;
    private Throwable exception;
    private LocalDateTime timestamp;
    private long duration;
    
    // getter/setter 和构造器
    // ...
    
    @Override
    public String toString() {
        return String.format("[%s] %s - %s.%s() 耗时:%dms %s",
            timestamp, action, className, methodName, duration,
            exception != null ? "异常:" + exception.getMessage() : "成功");
    }
}

// 审计切面
@Aspect
@Component
public class AuditAspect {
    
    private final List<AuditLog> auditLogs = new CopyOnWriteArrayList<>();
    
    @Around("@annotation(audit)")
    public Object audit(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
        AuditLog log = new AuditLog();
        log.setAction(audit.action());
        log.setDescription(audit.description());
        log.setClassName(joinPoint.getTarget().getClass().getSimpleName());
        log.setMethodName(joinPoint.getSignature().getName());
        log.setArgs(joinPoint.getArgs());
        log.setTimestamp(LocalDateTime.now());
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            log.setResult(result);
            return result;
        } catch (Throwable e) {
            log.setException(e);
            throw e;
        } finally {
            log.setDuration(System.currentTimeMillis() - startTime);
            auditLogs.add(log);
            System.out.println(log);
        }
    }
    
    public List<AuditLog> getAuditLogs() {
        return new ArrayList<>(auditLogs);
    }
}

// 使用示例
@Service
public class AccountService {
    
    @Audit(action = "转账", description = "用户转账操作")
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        System.out.println(String.format("转账: %d -> %d, 金额: %s", fromId, toId, amount));
    }
    
    @Audit(action = "开户", description = "创建新账户")
    public Long createAccount(String username) {
        System.out.println("创建账户: " + username);
        return System.currentTimeMillis();
    }
}

五、代理机制深入

5.1 JDK 动态代理示例

import java.lang.reflect.*;

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

// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public User findById(Long id) {
        System.out.println("UserServiceImpl.findById: " + id);
        return new User(id, "User-" + id);
    }
    
    @Override
    public void save(User user) {
        System.out.println("UserServiceImpl.save: " + user);
    }
}

// JDK 动态代理
public class JdkProxyDemo {
    
    public static void main(String[] args) {
        // 保存生成的代理类
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        
        UserService target = new UserServiceImpl();
        
        UserService proxy = (UserService) Proxy.newProxyInstance(
            JdkProxyDemo.class.getClassLoader(),
            new Class<?>[] { UserService.class },
            new LogInvocationHandler(target)
        );
        
        proxy.findById(1L);
        proxy.save(new User(2L, "Test"));
    }
}

// 调用处理器
class LogInvocationHandler implements InvocationHandler {
    private final Object target;
    
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("=== JDK 代理前 ===");
        System.out.println("方法: " + method.getName());
        System.out.println("参数: " + Arrays.toString(args));
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = method.invoke(target, args);
            System.out.println("返回: " + result);
            return result;
        } finally {
            System.out.println("耗时: " + (System.currentTimeMillis() - startTime) + " ms");
            System.out.println("=== JDK 代理后 ===\n");
        }
    }
}

5.2 CGLIB 代理示例

import org.springframework.cglib.proxy.*;

// 目标类(无接口)
public class OrderService {
    public void createOrder(String productId, int quantity) {
        System.out.println("创建订单: productId=" + productId + ", quantity=" + quantity);
    }
    
    public Order getOrder(Long id) {
        System.out.println("获取订单: " + id);
        return new Order(id, "Product-" + id);
    }
}

// CGLIB 代理
public class CglibProxyDemo {
    
    public static void main(String[] args) {
        // 保存生成的代理类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib");
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class);
        enhancer.setCallback(new LogMethodInterceptor());
        
        OrderService proxy = (OrderService) enhancer.create();
        
        proxy.createOrder("P001", 2);
        proxy.getOrder(1L);
    }
}

// 方法拦截器
class LogMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("=== CGLIB 代理前 ===");
        System.out.println("方法: " + method.getName());
        System.out.println("参数: " + Arrays.toString(args));
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 调用父类方法(避免递归)
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("返回: " + result);
            return result;
        } finally {
            System.out.println("耗时: " + (System.currentTimeMillis() - startTime) + " ms");
            System.out.println("=== CGLIB 代理后 ===\n");
        }
    }
}

5.3 代理选择策略

import org.springframework.context.annotation.*;
import org.springframework.aop.framework.*;

@Configuration
@EnableAspectJAutoProxy
public class ProxyConfig {
    
    // 强制使用 CGLIB 代理
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public static class ForceCglibConfig {}
    
    // 使用 JDK 代理(默认)
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    public static class JdkProxyConfig {}
}

// 代理检测工具
public class ProxyUtils {
    
    public static void printProxyInfo(Object bean) {
        Class<?> clazz = bean.getClass();
        
        if (AopUtils.isAopProxy(bean)) {
            System.out.println("是 AOP 代理");
            
            if (AopUtils.isCglibProxy(bean)) {
                System.out.println("代理类型: CGLIB");
                System.out.println("父类: " + clazz.getSuperclass().getSimpleName());
            } else if (AopUtils.isJdkDynamicProxy(bean)) {
                System.out.println("代理类型: JDK 动态代理");
                System.out.println("接口: " + Arrays.toString(clazz.getInterfaces()));
            }
            
            // 获取目标类
            try {
                Object target = AopProxyUtils.getSingletonTarget(bean);
                System.out.println("目标类: " + target.getClass().getSimpleName());
            } catch (Exception e) {
                System.out.println("无法获取目标类");
            }
        } else {
            System.out.println("不是代理");
        }
    }
}

六、总结与最佳实践

最佳实践

  1. 切面设计原则

    • 单一职责:每个切面只关注一个横切关注点
    • 命名清晰:切面和方法名要有意义
    • 避免过度使用:不是所有需求都适合 AOP
  2. 切点表达式优化

    • 使用命名切点,提高可读性
    • 避免过于宽泛的切点表达式
    • 合理使用组合表达式
  3. 通知选择

    • 优先使用最简单的通知类型
    • Around 最强大但也最复杂
    • 注意通知的执行顺序
  4. 代理机制选择

    • 有接口:JDK 动态代理
    • 无接口:CGLIB
    • Spring Boot 默认使用 CGLIB

常见陷阱

  1. 内部方法调用

    • 问题:同类内部方法调用不会触发代理
    • 解决:注入自身或使用 AopContext
  2. final 方法

    • 问题:CGLIB 无法代理 final 方法
    • 解决:移除 final 修饰符
  3. private 方法

    • 问题:Spring AOP 只代理 public 方法
    • 解决:改为 public 或使用 AspectJ
  4. 通知顺序混乱

    • 问题:多切面时执行顺序不确定
    • 解决:使用 @Order 注解

Spring AOP 是实现横切关注点分离的强大工具。理解其原理和最佳实践,能够帮助我们更好地实现日志、事务、权限等功能,提高代码的可维护性和可扩展性。