面向切面编程(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 框架的灵魂特性,从声明式事务到安全认证,从日志记录到性能监控,无处不在。深入理解其代理机制和最佳实践,才能写出高效、可维护的切面代码。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。