一、什么是AOP?
AOP(面向切面编程)是一种编程范式,用于将横切关注点(如日志、事务、安全等)与核心业务逻辑分离的技术。
核心思想:
- 关注点分离:将系统的通用功能(横切关注点)从业务逻辑中抽离
- 模块化:通过切面将这些通用功能模块化,便于复用和维护
- 解耦:避免业务代码中混入大量重复的通用代码
二、AOP的核心概念
|
概念 |
说明 |
类比 |
|
Aspect(切面) |
横切关注点的模块化,如日志切面、事务切面 |
一个专门的“功能模块” |
|
Join Point(连接点) |
程序执行过程中的某个点,如方法调用、异常抛出 |
可以被增强的“时机点” |
|
Pointcut(切点) |
匹配连接点的表达式,定义在哪些地方应用切面 |
“筛选器”,选择哪些连接点要增强 |
|
Advice(通知) |
在特定连接点执行的动作 |
增强的具体“行为” |
|
Weaving(织入) |
将切面应用到目标对象创建代理对象的过程 |
“植入”增强逻辑的过程 |
三、Advice的类型
java
// 1. Before:方法执行前 @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice() { System.out.println("方法执行前"); } // 2. AfterReturning:方法正常返回后 @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result") public void afterReturning(Object result) { System.out.println("方法返回: " + result); } // 3. AfterThrowing:方法抛出异常后 @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex") public void afterThrowing(Exception ex) { System.out.println("方法异常: " + ex.getMessage()); } // 4. After(Finally):方法执行后(无论成功还是异常) @After("execution(* com.example.service.*.*(..))") public void afterAdvice() { System.out.println("方法执行结束"); } // 5. Around:环绕通知(最强大) @Around("execution(* com.example.service.*.*(..))") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { // 方法执行前 System.out.println("开始执行: " + joinPoint.getSignature()); long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); // 执行目标方法 // 方法执行后 long end = System.currentTimeMillis(); System.out.println("执行耗时: " + (end - start) + "ms"); return result; }
四、具体例子:日志切面
场景:为所有Service方法添加执行日志和耗时统计
java
@Component @Aspect public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); // 定义切点:匹配所有Service包下的方法 @Pointcut("execution(* com.example.service..*.*(..))") public void serviceLayer() {} // 环绕通知:记录方法执行耗时 @Around("serviceLayer()") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String className = signature.getDeclaringType().getSimpleName(); String methodName = signature.getName(); long start = System.currentTimeMillis(); try { logger.info("[开始执行] {}.{}() 参数: {}", className, methodName, Arrays.toString(joinPoint.getArgs())); Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); logger.info("[执行成功] {}.{}() 耗时: {}ms 结果: {}", className, methodName, (end - start), result); return result; } catch (Exception e) { long end = System.currentTimeMillis(); logger.error("[执行异常] {}.{}() 耗时: {}ms 异常: {}", className, methodName, (end - start), e.getMessage(), e); throw e; } } // 方法执行前的通知 @Before("serviceLayer() && @annotation(org.springframework.transaction.annotation.Transactional)") public void logBeforeTransactionalMethod(JoinPoint joinPoint) { logger.debug("准备执行事务方法: {}", joinPoint.getSignature()); } }
五、实际应用场景
1.事务管理(Spring最经典的AOP应用)
java
@Service public class OrderService { @Transactional public void createOrder(Order order) { // 业务逻辑 orderDao.save(order); inventoryService.reduceStock(order); // 如果出现异常,整个事务会回滚 } }
2.权限控制
java
@Aspect @Component public class SecurityAspect { @Before("@annotation(RequiresPermission)") public void checkPermission(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); RequiresPermission annotation = signature.getMethod() .getAnnotation(RequiresPermission.class); String permission = annotation.value(); if (!SecurityContext.hasPermission(permission)) { throw new UnauthorizedException("缺少权限: " + permission); } } } // 使用 @RequiresPermission("order:create") public void createOrder(Order order) { // 需要权限的业务逻辑 }
3.缓存切面
java
@Aspect @Component public class CacheAspect { @Around("@annotation(cacheable)") public Object cacheResult(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable { String key = generateCacheKey(joinPoint); Object cachedValue = cache.get(key); if (cachedValue != null) { return cachedValue; } Object result = joinPoint.proceed(); cache.put(key, result, cacheable.ttl()); return result; } }
六、Spring AOP的实现原理
底层技术:
- JDK动态代理(默认,针对接口)
- CGLIB动态代理(针对类,当目标对象没有实现接口时)
织入时机:
- 编译时织入(AspectJ)
- 类加载时织入(AspectJ)
- 运行时织入(Spring AOP采用的方式)
七、使用建议
优点:
✅ 减少重复代码:横切关注点只需编写一次 ✅ 提高可维护性:业务逻辑更清晰 ✅ 灵活配置:通过注解或XML配置切面 ✅ 易于测试:切面可以独立测试
注意事项:
⚠️ 性能开销:代理调用比直接调用稍慢 ⚠️ 仅方法拦截:Spring AOP只能拦截方法,不能拦截字段访问 ⚠️ 自调用问题:同一类内方法互相调用,AOP可能失效 ⚠️ 理解复杂度:需要理解代理机制和AOP概念
总结
Spring AOP通过动态代理技术,在不修改原有代码的情况下,为程序添加额外的功能。它特别适合处理横切关注点,如日志、事务、安全、缓存等。理解AOP的核心概念(切面、切点、通知)和实际应用场景,能帮助我们编写更清晰、更易维护的代码。
887

被折叠的 条评论
为什么被折叠?



