Spring AOP 原理剖析与 AspectJ 企业级实战

深入 Spring AOP 的代理机制与底层实现,掌握 AspectJ 切点表达式、环绕通知与编译时织入,构建高性能横切面基础设施

面向切面编程(AOP)是 Spring 框架的核心能力之一,它允许开发者将横切关注点(日志、事务、安全 、监控)从业务逻辑中分离,实现代码的模块化与复用。理解 AOP 的底层原理,是掌握 Spring 框架的关键一步。

一、AOP 核心概念

1.1 术语体系

术语英文说明
切面Aspect横切关注点的模块化封装
连接点Join Point程序执行过程中的某个点(方法调用、异常抛出)
切点Pointcut匹配连接点的断言表达式
通知Advice在切点处执行的增强逻辑
目标对象Target被代理的原始对象
织入Weaving将切面应用到目标对象的过程

1.2 通知类型

@Aspect
@Component
public class LogAspect {
    
    @Before("execution(* com.example.service.*.*(..))")      // 方法执行前
    public void before(JoinPoint jp) {
        log.info("[Before] 方法: {}", jp.getSignature().getName());
    }
    
    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void afterReturn(JoinPoint jp, Object result) {     // 方法正常返回后
        log.info("[AfterReturning] 结果: {}", result);
    }
    
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void afterThrow(JoinPoint jp, Exception ex) {       // 方法抛出异常后
        log.error("[AfterThrowing] 异常: {}", ex.getMessage());
    }
    
    @After("execution(* com.example.service.*.*(..))")        // 方法最终执行(无论是否异常)
    public void after(JoinPoint jp) {
        log.info("[After] 方法结束: {}", jp.getSignature().getName());
    }
    
    @Around("execution(* com.example.service.*.*(..))")       // 环绕通知(最强大)
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();  // 执行目标方法
        } finally {
            log.info("[Around] 耗时: {}ms", System.currentTimeMillis() - start);
        }
    }
}

二、代理机制深度解析

2.1 JDK 动态代理 vs CGLIB

特性JDK 动态代理CGLIB
原理实现 InvocationHandler继承目标类生成子类
要求目标类必须实现接口目标类不能是 final
性能反射调用,稍慢FastClass 优化,较快
可见性只能代理接口方法可代理 public/protected 方法
Spring 默认有接口时用无接口时用;或强制配置

2.2 JDK 代理原理

// 相当于 Spring 生成的代理
public class OrderServiceProxy implements OrderService, SpringProxy {
    
    private final OrderService target;
    private final InvocationHandler handler;
    
    @Override
    public Order createOrder(Request req) {
        Method method = OrderService.class.getMethod("createOrder", Request.class);
        return (Order) handler.invoke(this, method, new Object[]{req});
    }
}

// InvocationHandler 实现
public class TransactionHandler implements InvocationHandler {
    
    private final Object target;
    private final PlatformTransactionManager txManager;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TransactionStatus status = txManager.getTransaction(new DefaultTransactionDefinition());
        try {
            Object result = method.invoke(target, args);
            txManager.commit(status);
            return result;
        } catch (Exception e) {
            txManager.rollback(status);
            throw e;
        }
    }
}

2.3 CGLIB 代理原理

// CGLIB 生成的代理类(简化示意)
public class OrderService$$EnhancerBySpringCGLIB extends OrderService {
    
    private MethodInterceptor methodInterceptor;  // CGLIB 的 MethodInterceptor
    private static final Method createOrder$Method;
    
    static {
        createOrder$Method = ReflectionUtils.findMethod(OrderService.class, "createOrder");
    }
    
    @Override
    public Order createOrder(Request req) {
        MethodProxy methodProxy = MethodProxy.create(
            OrderService.class, 
            OrderService$$EnhancerBySpringCGLIB.class,
            "()Lcom/example/Order;",
            "createOrder",
            "createOrder$super"
        );
        return (Order) methodInterceptor.intercept(this, createOrder$Method, new Object[]{req}, methodProxy);
    }
    
    // FastClass 优化,避免反射
    final Order createOrder$super(Request req) {
        return super.createOrder(req);
    }
}

2.4 Spring 代理选择逻辑

// 在 DefaultAopProxyFactory 中
public class DefaultAopProxyFactory implements AopProxyFactory {
    
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);    // JDK 代理
            }
            return new ObjenesisCglibAopProxy(config);    // CGLIB 代理
        }
        return new JdkDynamicAopProxy(config);
    }
}

强制使用 CGLIB:

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)  // 强制 CGLIB
public class Application { }

三、切点表达式详解

3.1 execution 表达式

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

// 示例
@Pointcut("execution(public * com.example.service.*.*(..))")           // service 包下所有 public 方法
@Pointcut("execution(* com.example..*Service.*(..))")                  // 任意子包下以 Service 结尾的类
@Pointcut("execution(* *(..))")                                         // 任意方法(慎用)
@Pointcut("execution(* save*(..))")                                    // 以 save 开头的方法
@Pointcut("execution(String com.example.service.UserService.find*(..))") // 返回 String,以 find 开头
@Pointcut("execution(* com.example.service.*.*(Long, ..))")           // 第一个参数是 Long

3.2 其他指示器

@Pointcut("@annotation(com.example.annotation.Loggable)")      // 带特定注解的方法
@Pointcut("@within(com.example.annotation.ServiceLayer)")      // 类上有注解(对类内所有方法生效)
@Pointcut("bean(*Service)")                                     // Bean 名称匹配
@Pointcut("within(com.example.service.*)")                     // 包匹配(不含子包)
@Pointcut("within(com.example.service..*)")                    // 包匹配(含子包)
@Pointcut("args(java.io.Serializable)")                        // 参数类型匹配
@Pointcut("target(com.example.service.OrderService)")          // 目标对象类型

3.3 组合切点

@Aspect
@Component
public class ComplexPointcuts {
    
    @Pointcut("execution(* com.example.controller.*.*(..))")
    public void controllerLayer() {}
    
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    @Pointcut("@annotation(com.example.annotation.RequireAuth)")
    public void requireAuth() {}
    
    // 交集:controller 层且需要认证
    @Pointcut("controllerLayer() && requireAuth()")
    public void authController() {}
    
    // 并集:controller 或 service 层
    @Pointcut("controllerLayer() || serviceLayer()")
    public void businessLayer() {}
    
    // 差集:controller 层但不需要认证
    @Pointcut("controllerLayer() && !requireAuth()")
    public void publicController() {}
}

四、环绕通知最佳实践

4.1 统一异常处理切面

@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)  // 最高优先级
global class GlobalExceptionAspect {
    
    @Around("execution(* com.example.controller.*.*(..))")
    public Object handleException(ProceedingJoinPoint pjp) {
        try {
            return pjp.proceed();
        } catch (BusinessException e) {
            log.warn("业务异常: {}", e.getMessage());
            return ApiResult.fail(e.getCode(), e.getMessage());
        } catch (ValidationException e) {
            log.warn("参数校验失败: {}", e.getMessage());
            return ApiResult.fail(400, e.getMessage());
        } catch (Exception e) {
            log.error("系统异常", e);
            return ApiResult.fail(500, "系统繁忙,请稍后重试");
        }
    }
}

4.2 分布式锁切面

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {
    String key();                    // 锁的 key,支持 SpEL
    long waitTime() default 5;       // 等待时间(秒)
    long leaseTime() default 30;     // 持有时间(秒)
}

@Aspect
@Component
public class DistributedLockAspect {
    
    @Autowired
    private RedissonClient redisson;
    
    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Throwable {
        String key = parseSpel(distributedLock.key(), pjp);
        RLock lock = redisson.getLock(key);
        
        boolean acquired = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), TimeUnit.SECONDS);
        if (!acquired) {
            throw new LockException("获取锁失败: " + key);
        }
        
        try {
            return pjp.proceed();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    
    private String parseSpel(String expression, ProceedingJoinPoint pjp) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("args", pjp.getArgs());
        return new SpelExpressionParser().parseExpression(expression).getValue(context, String.class);
    }
}

// 使用
@Service
public class StockService {
    
    @DistributedLock(key = "'stock:' + #skuId", waitTime = 3, leaseTime = 10)
    public void deductStock(String skuId, int qty) {
        // 扣减库存逻辑
    }
}

4.3 方法耗时监控切面

@Aspect
@Component
public class PerformanceAspect {
    
    private final MeterRegistry meterRegistry;
    
    @Around("execution(* com.example..*(..)) && !execution(* com.example.config..*(..))")
    public Object measure(ProceedingJoinPoint pjp) throws Throwable {
        Timer.Sample sample = Timer.start(meterRegistry);
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();
        
        try {
            return pjp.proceed();
        } finally {
            sample.stop(meterRegistry.timer("method.execution",
                "class", className,
                "method", methodName));
        }
    }
}

五、AspectJ 编译时织入

5.1 Spring AOP 的局限

局限说明
仅支持方法级无法拦截字段修改、构造器调用
仅支持 Spring Bean非 Bean 对象不受代理
内部调用问题this.method() 不走代理
性能开销代理调用比直接调用慢 3-10 倍

5.2 AspectJ LTW(加载时织入)

<!-- pom.xml -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class AspectjConfig {}
// JVM 参数
-javaagent:/path/to/aspectjweaver.jar

5.3 AspectJ 语法增强

public aspect TransactionAspect {
    
    // 拦截构造器
    pointcut init(): execution(com.example.service.*.new(..));
    
    // 拦截字段设置
    pointcut fieldSet(): set(* com.example.model.*.*);
    
    // 拦截异常处理
    pointcut handler(): handler(Exception+);
    
    // 编译时织入,无代理开销
    Object around(): execution(@Transactional * *(..)) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            Object result = proceed();
            transactionManager.commit(status);
            return result;
        } catch (RuntimeException e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

六、内部调用问题与解决

6.1 问题现象

@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        saveOrder(order);
        this.updateInventory(order);  // 内部调用,不走代理!
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory(Order order) {
        // 期望在新事务中执行,实际不会生效
    }
}

6.2 解决方案

@Service
public class OrderService {
    
    @Autowired
    private ApplicationContext context;
    
    @Transactional
    public void createOrder(Order order) {
        saveOrder(order);
        // 通过容器获取代理对象
        OrderService proxy = context.getBean(OrderService.class);
        proxy.updateInventory(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory(Order order) {
        // 现在会在新事务中执行
    }
}

// 更好的方案:重构为两个 Service
@Service
public class OrderService {
    @Autowired private InventoryService inventoryService;
    
    @Transactional
    public void createOrder(Order order) {
        saveOrder(order);
        inventoryService.update(order);  // 跨 Bean 调用,走代理
    }
}

七、性能优化建议

建议说明
缩小切点范围避免 execution(* *(..)) 这种全量匹配
优先使用 @Around一个 @Around 可替代多个单一通知
减少反射操作缓存 Method 对象,避免重复查找
避免循环依赖AOP 代理会加剧循环依赖问题
考虑编译时织入对性能极敏感场景使用 AspectJ

八、总结

主题要点
代理机制JDK 接口代理 vs CGLIB 子类代理
切点设计execution + 组合表达式,精确匹配
通知选择优先 @Around,注意执行顺序
内部调用通过代理或重构解决
性能优化缩小切面范围,考虑编译时织入

AOP 是 Spring 框架的灵魂特性,从声明式事务到安全认证,从日志记录到性能监控,无处不在。深入理解其代理机制和最佳实践,才能写出高效、可维护的切面代码。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java-enterprise」更多文章

  1. JPMS 模块化:module-info 与 JLink 精简运行时
  2. CDC 数据同步:Debezium 与 Kafka 架构实战
  3. 可观测性工程:Micrometer 指标模型与 OTLP 导出