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. 限流算法深度解析:令牌桶、漏桶与滑动窗口计数
  2. Java 代码质量:SonarQube、Checkstyle 与 SpotBugs 工程化实践
  3. Spring IoC 容器与依赖注入原理深度剖析