Spring AOP与实践指南
一、为什么需要AOP编程?
在传统OOP编程中,核心业务逻辑与系统级服务(如日志记录、事务管理、权限校验等)高度耦合,导致以下问题:
- 代码重复:相同校验逻辑散落在多个方法中
- 维护困难:修改安全策略需要改动所有相关方法
- 关注点混杂:核心业务代码被非功能需求污染
// 典型OOP代码示例
public class OrderService {
public void createOrder(Order order) {
// 权限校验
if (!SecurityContext.hasPermission("ORDER_CREATE")) {
throw new AccessDeniedException();
}
// 日志记录
System.out.println("开始创建订单:" + order.getId());
long start = System.currentTimeMillis();
// 核心业务逻辑
orderDao.save(order);
inventoryService.deductStock(order);
// 性能监控
long duration = System.currentTimeMillis() - start;
System.out.println("订单创建耗时:" + duration + "ms");
}
}
二、AOP核心概念与实现机制
1. 代理模式深度解析
Spring AOP基于动态代理实现,主要分为两种方式:
JDK动态代理(接口代理):
- 通过Proxy.newProxyInstance()创建代理对象
- 要求目标类必须实现至少一个接口
- 生成的代理类继承Proxy类
public class JdkProxyDemo {
interface Service {
void execute();
}
static class Target implements Service {
public void execute() {
System.out.println("实际业务执行");
}
}
public static void main(String[] args) {
Service proxy = (Service) Proxy.newProxyInstance(
JdkProxyDemo.class.getClassLoader(),
new Class[]{Service.class},
(p, method, args1) -> {
System.out.println("前置处理");
Object result = method.invoke(new Target(), args1);
System.out.println("后置处理");
return result;
});
proxy.execute();
}
}
CGLIB代理(子类代理):
- 通过Enhancer类生成目标类的子类
- 不需要接口支持
- final类/方法无法被代理
- 性能略优于JDK代理(Java 8+)
public class CglibProxyDemo {
static class Target {
public void execute() {
System.out.println("实际业务执行");
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Target.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
System.out.println("前置处理");
Object result = proxy.invokeSuper(obj, args1);
System.out.println("后置处理");
return result;
});
Target proxy = (Target) enhancer.create();
proxy.execute();
}
}
2. 切面编程核心术语
- JoinPoint(连接点):程序执行过程中的特定点(方法调用、异常抛出等)
- Pointcut(切点):匹配连接点的表达式
- Advice(通知):在特定连接点执行的动作
- Aspect(切面):通知+切点的模块化单元
- Weaving(织入):将切面应用到目标对象的过程
三、Spring AOP实战配置
1. 基于注解的AOP配置
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// 配置其他Bean...
}
@Aspect
@Component
public class LoggingAspect {
// 匹配com.example.service包下所有类的public方法
@Pointcut("execution(public * com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void logMethodEntry(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("进入方法:" + methodName);
}
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logMethodExit(JoinPoint joinPoint, Object result) {
System.out.println("方法返回:" + result);
}
@Around("serviceLayer()")
public Object measureExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
Object result = pjp.proceed();
long duration = System.nanoTime() - start;
System.out.println("方法执行耗时:" + duration + "ns");
return result;
}
}
2. 常用Pointcut表达式
-
方法签名匹配:
@Pointcut("execution(* com.example.service.*.*(..))") // 任意返回类型,service包下所有方法 @Pointcut("execution(public * *(..))") // 所有public方法 @Pointcut("execution(* save*(..))") // 所有以save开头的方法
-
注解驱动匹配:
@Pointcut("@annotation(com.example.Loggable)") // 方法带有@Loggable注解 @Pointcut("@within(org.springframework.stereotype.Service)") // 类级别注解匹配
-
参数匹配:
@Pointcut("args(java.io.Serializable)") // 参数包含Serializable类型 @Pointcut("@args(com.example.Validated)") // 参数带有指定注解
四、高级AOP特性应用
1. 自定义注解实现声明式编程
定义业务注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RetryOperation {
int maxAttempts() default 3;
Class<? extends Throwable>[] retryExceptions() default {Exception.class};
}
实现重试切面:
@Aspect
@Component
public class RetryAspect {
@Around("@annotation(retryOperation)")
public Object retryOperation(ProceedingJoinPoint pjp, RetryOperation retryOperation) throws Throwable {
int attempts = 0;
Throwable lastError;
do {
try {
return pjp.proceed();
} catch (Throwable ex) {
if (!Arrays.asList(retryOperation.retryExceptions()).contains(ex.getClass())) {
throw ex;
}
lastError = ex;
attempts++;
System.out.println("操作失败,进行第" + attempts + "次重试");
}
} while (attempts < retryOperation.maxAttempts());
throw lastError;
}
}
2. 异常处理最佳实践
@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex"
)
public void handleServiceExceptions(Exception ex) {
if (ex instanceof BusinessException) {
// 处理业务异常
System.err.println("业务异常:" + ex.getMessage());
} else {
// 记录系统异常
System.err.println("系统异常:" + ex.getClass().getName());
ex.printStackTrace();
}
}
}
3. AOP执行顺序控制
@Aspect
@Order(1)
public class LoggingAspect {
// 最先执行的切面
}
@Aspect
@Order(2)
public class SecurityAspect {
// 其次执行的切面
}
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE)
public class MonitoringAspect {
// 最后执行的切面
}
五、性能优化与陷阱规避
1. 代理对象识别问题
错误示范:
@Service
public class TransactionService {
@Transactional
public void outerMethod() {
innerMethod(); // 事务注解失效!
}
@Transactional
public void innerMethod() {
// 数据库操作
}
}
正确方案:
@Service
public class TransactionService {
@Autowired
private TransactionService selfProxy; // 注入代理对象
public void outerMethod() {
selfProxy.innerMethod(); // 通过代理调用
}
@Transactional
public void innerMethod() {
// 数据库操作
}
}
2. 循环依赖解决方案
配置参数调整:
# application.properties
spring.aop.proxy-target-class=true # 强制使用CGLIB代理
3. 性能优化策略
- 减少Pointcut匹配复杂度
- 避免在通知方法中执行耗时操作
- 合理使用@Around替代多个通知组合
- 优先选择基于接口的JDK动态代理
// 性能优化示例:缓存切点匹配结果
@Aspect
public class CachingAspect {
private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
@Around("execution(* com.example.service.*.*(..)) && @annotation(cacheable)")
public Object cacheResult(ProceedingJoinPoint pjp, Cacheable cacheable) throws Throwable {
String cacheKey = generateCacheKey(pjp);
Object result = cache.get(cacheKey);
if (result == null) {
result = pjp.proceed();
cache.put(cacheKey, result);
}
return result;
}
private String generateCacheKey(JoinPoint joinPoint) {
// 生成唯一缓存键
}
}
正文到此结束
相关文章
热门推荐
评论插件初始化中...