Spring AOP实战指南:面向切面编程精髓
文章目录
- 前言
- 一、AOP应用场景
- 二、AOP核心概念解析
- 三、代理模式原理对比
- 1.JDK动态代理
- 2.CGLIB代理
- 四、AOP配置全方案
- 1.XML声明式配置
- 2.注解驱动配置
- 四、切面编程进阶技巧
- 1.切入点表达式优化
- 2.通知执行顺序控制
- 六、生产环境避坑指南
- 1.代理失效场景
- 2.性能优化策略
- 3.日志切面最佳实践
- 七、 Spring AOP五种通知类型
- 1. @Before 前置通知
- 2. @AfterReturning 返回通知
- 3. @AfterThrowing 异常通知
- 4. @After 最终通知
- 5. @Around 环绕通知(最强大)
- 总结
前言
面向切面编程(AOP)通过横向切割系统功能,将日志、事务等横切关注点与核心业务解耦,无需侵入代码即可实现功能增强,是构建高内聚系统的关键设计范式。
一、AOP应用场景
应用场景 | 实现方式 | 业务收益 |
---|---|---|
事务管理 | @Transactional 注解 | 保证数据原子性与一致性 |
权限校验 | 前置通知(@Before ) | 实现接口访问安全隔离 |
性能监控 | 环绕通知+耗时计算 | 提供系统优化量化指标 |
日志追踪 | 切点表达式+MDC上下文 | 全链路日志快速定位异常 |
二、AOP核心概念解析
术语 | 说明 |
---|---|
切面(Aspect) | 切入点 + 通知的集合(功能增强模块) |
连接点(JoinPoint) | 程序执行中可插入切面的点(如方法执行) |
切入点(Pointcut) | 实际被增强的连接点(通过表达式筛选) |
通知(Advice) | 增强的具体行为(前/后/环绕/异常等类型) |
技术说明:
MDC
:Mapped Diagnostic Context,日志框架的上下文跟踪技术- 环绕通知可自由控制目标方法执行时机,适合耗时统计等场景
@Transactional
通过AOP自动管理数据库事务边界
三、代理模式原理对比
1.JDK动态代理
- 要求目标类实现接口
- 运行时生成$Proxy类
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
2.CGLIB代理
- 通过继承实现代理
- 无需接口支持
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback(new MethodInterceptor());
四、AOP配置全方案
1.XML声明式配置
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut id="serviceMethods"
expression="execution(* com.service..*(..))"/>
<aop:before method="logParams"
pointcut-ref="serviceMethods"/>
<aop:after-returning method="logResult"
returning="result"
pointcut-ref="serviceMethods"/>
</aop:aspect>
</aop:config>
2.注解驱动配置
@Aspect
@Component
public class PerformanceAspect {
private ThreadLocal<Long> timer = new ThreadLocal<>();
@Around("@annotation(MonitorPerformance)")
public Object trackTime(ProceedingJoinPoint pjp) throws Throwable {
try {
timer.set(System.currentTimeMillis());
return pjp.proceed();
} finally {
long duration = System.currentTimeMillis() - timer.get();
log.info("方法 {} 执行耗时: {}ms",
pjp.getSignature(), duration);
}
}
}
四、切面编程进阶技巧
1.切入点表达式优化
// 精确匹配服务层方法
@Pointcut("within(com.example.service.*Service)")
public void serviceLayer() {}
// 匹配特定注解
@Pointcut("@annotation(com.example.SensitiveOperation)")
public void sensitiveOperations() {}
// 组合条件
@Pointcut("serviceLayer() && sensitiveOperations()")
public void criticalOperations() {}
2.通知执行顺序控制
@Aspect
@Order(1) // 控制切面执行顺序
public class ValidationAspect {
@Before("execution(* com.example.service.*.*(..))")
public void validateInputs() {
// 参数校验逻辑
}
}
六、生产环境避坑指南
1.代理失效场景
- 同类内部方法调用
- private方法增强
- final类代理
2.性能优化策略
- 编译时织入(AspectJ)
- 避免在切面中处理重逻辑
- 合理设置切面粒度
3.日志切面最佳实践
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger =
LoggerFactory.getLogger(LoggingAspect.class);
@Around("execution(* com.example..*(..))")
public Object logMethod(ProceedingJoinPoint pjp) throws Throwable {
MDC.put("traceId", UUID.randomUUID().toString());
try {
logger.debug(">> {} 参数: {}",
pjp.getSignature(),
Arrays.toString(pjp.getArgs()));
Object result = pjp.proceed();
logger.debug("<< {} 返回值: {}",
pjp.getSignature(),
result);
return result;
} finally {
MDC.clear();
}
}
}
七、 Spring AOP五种通知类型
1. @Before 前置通知
执行时机:目标方法执行前
典型应用:权限校验、日志记录
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("【前置通知】准备执行方法: " + methodName);
}
}
2. @AfterReturning 返回通知
执行时机:方法正常返回后
返回值获取:通过returning参数
@Aspect
@Component
public class AuditAspect {
@AfterReturning(
pointcut = "execution(* com.example.service.*.get*(..))",
returning = "result"
)
public void afterReturning(Object result) {
System.out.println("【返回通知】方法返回值: " + result);
}
}
3. @AfterThrowing 异常通知
执行时机:方法抛出异常时
异常捕获:通过throwing参数
@Aspect
@Component
public class ErrorAspect {
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex"
)
public void afterThrowing(JoinPoint jp, Exception ex) {
String method = jp.getSignature().toShortString();
System.out.println("【异常通知】"+method+" 发生异常: "+ex.getMessage());
}
}
4. @After 最终通知
执行时机:方法执行结束(无论成功/异常)
资源清理:类似finally块
@Aspect
@Component
public class CleanupAspect {
@After("execution(* com.example.service.*.process*(..))")
public void afterFinally() {
System.out.println("【最终通知】执行资源清理操作");
}
}
5. @Around 环绕通知(最强大)
核心能力:
- 控制方法执行时机
- 修改参数/返回值
- 异常处理
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
// 前置处理
System.out.println("【环绕前】开始执行");
// 执行目标方法
Object result = pjp.proceed();
// 后置处理
System.out.println("【环绕后】正常返回");
return result;
} catch (Exception e) {
// 异常处理
System.out.println("【环绕异常】错误信息: "+e.getMessage());
throw e;
} finally {
// 最终处理
long cost = System.currentTimeMillis() - start;
System.out.println("【性能监控】方法执行耗时: "+cost+"ms");
}
}
}
总结
AOP以动态代理为核心,通过切面统一管理通用逻辑,显著提升代码复用性和可维护性,其“声明式编程”思想为复杂系统提供优雅的扩展方案。