Spring AOP实现原理
约 1622 字大约 5 分钟
springaop
2025-03-22
概述
AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的核心特性之一。它通过将横切关注点(如日志、事务、安全)与业务逻辑分离,降低模块间耦合度。Spring AOP 基于代理模式实现,在运行时为目标对象创建代理对象,拦截方法调用并织入增强逻辑。
AOP 核心概念
| 概念 | 说明 |
|---|---|
| JoinPoint | 程序执行的某个位置,Spring AOP 仅支持方法级别的 JoinPoint |
| Pointcut | 定义拦截规则,匹配哪些方法会被增强 |
| Advice | 在匹配的 JoinPoint 执行的增强逻辑 |
| Aspect | 切面,Pointcut + Advice 的模块化封装 |
| Advisor | Spring 内部的切面表示,关联一个 Pointcut 和一个 Advice |
两种代理机制
Spring AOP 采用两种动态代理技术:JDK 动态代理和 CGLIB 代理。
JDK 动态代理
基于 java.lang.reflect.Proxy,要求目标类必须实现接口。代理对象实现与目标对象相同的接口。
public class JdkProxyDemo {
interface UserService {
void saveUser(String name);
}
static class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("Saving user: " + name);
}
}
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxyObj, method, methodArgs) -> {
System.out.println("[Before] " + method.getName());
Object result = method.invoke(target, methodArgs);
System.out.println("[After] " + method.getName());
return result;
}
);
proxy.saveUser("Alice");
}
}CGLIB 代理
基于 ASM 字节码框架,通过生成目标类的子类实现代理。不需要目标类实现接口,但无法代理 final 类和 final 方法。
public class CglibProxyDemo {
static class OrderService {
public void createOrder(String orderId) {
System.out.println("Creating order: " + orderId);
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, methodArgs, proxy) -> {
System.out.println("[Before] " + method.getName());
Object result = proxy.invokeSuper(obj, methodArgs);
System.out.println("[After] " + method.getName());
return result;
});
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("ORD-001");
}
}对比总结
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 前提条件 | 目标类需实现接口 | 无需接口 |
| 实现方式 | java.lang.reflect.Proxy | 字节码生成子类 |
| 代理类型 | 接口代理 | 类代理(继承) |
| final 方法 | 可代理(接口方法) | 不可代理 |
| 性能 | 方法调用通过反射,较慢 | 方法调用通过 FastClass,较快 |
| Spring 默认 | SpringBoot 2.x+ 默认 CGLIB | 可通过配置切换 |
注意: 从 Spring Boot 2.0 开始,默认使用 CGLIB 代理(
spring.aop.proxy-target-class=true)。
ProxyFactory —— 代理工厂
ProxyFactory 是 Spring AOP 的核心 API,统一了 JDK 和 CGLIB 两种代理的创建过程。
// 使用 ProxyFactory 创建代理
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new UserServiceImpl());
proxyFactory.addInterface(UserService.class);
// 添加通知
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before method: " + invocation.getMethod().getName());
Object result = invocation.proceed();
System.out.println("After method: " + invocation.getMethod().getName());
return result;
}
});
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.saveUser("Bob");Advisor / Pointcut / Advice 体系
Advice 类型
// 1. 前置通知
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature().getName());
}
// 2. 后置通知(无论是否异常都执行)
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature().getName());
}
// 3. 返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))",
returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("Returned: " + result);
}
// 4. 异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
System.out.println("Exception: " + ex.getMessage());
}
// 5. 环绕通知(最强大的通知类型)
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
return result;
} finally {
long elapsed = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature().getName() + " took " + elapsed + "ms");
}
}Pointcut 表达式
// execution - 匹配方法执行
@Pointcut("execution(public * com.example.service.UserService.find*(..))")
public void userFindMethods() {}
// within - 匹配指定类型内的所有方法
@Pointcut("within(com.example.service..*)")
public void serviceLayer() {}
// @annotation - 匹配带指定注解的方法
@Pointcut("@annotation(com.example.annotation.Loggable)")
public void loggableMethods() {}
// @within - 匹配带指定注解的类的所有方法
@Pointcut("@within(org.springframework.stereotype.Service)")
public void serviceBeans() {}
// args - 匹配参数类型
@Pointcut("execution(* com.example.service.*.*(..)) && args(String, ..)")
public void methodsWithStringArg() {}
// bean - 匹配指定Bean名称(Spring扩展)
@Pointcut("bean(userService)")
public void userServiceBean() {}
// 组合表达式
@Pointcut("serviceLayer() && !loggableMethods()")
public void nonLoggableServiceMethods() {}AspectJ 注解驱动 AOP
完整的切面定义示例:
@Aspect
@Component
public class PerformanceAspect {
private static final Logger log = LoggerFactory.getLogger(PerformanceAspect.class);
@Pointcut("@within(org.springframework.stereotype.Service)")
public void serviceLayer() {}
@Around("serviceLayer()")
public Object measurePerformance(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().toShortString();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
return pjp.proceed();
} catch (Throwable t) {
log.error("Exception in {}: {}", methodName, t.getMessage());
throw t;
} finally {
stopWatch.stop();
if (stopWatch.getTotalTimeMillis() > 500) {
log.warn("Slow method detected: {} took {}ms",
methodName, stopWatch.getTotalTimeMillis());
}
}
}
}AOP 代理创建流程
常见陷阱
1. 自调用问题
同一类中方法 A 调用方法 B,方法 B 的 AOP 不会生效,因为自调用不经过代理对象:
@Service
public class OrderService {
@Transactional
public void createOrder() {
// 直接调用,不经过代理,AOP不生效
this.validateOrder();
}
@Cacheable("orderValidation")
public boolean validateOrder() {
return true;
}
}解决方案: 注入自身引用或使用 AopContext:
@Service
public class OrderService {
@Autowired
private ObjectProvider<OrderService> selfProvider;
public void createOrder() {
selfProvider.getObject().validateOrder();
}
}2. private 方法无法被代理
Spring AOP 基于代理,只能拦截 public 方法。private、protected 方法和 final 方法无法被增强。
3. Advice 执行顺序
同一切面内,多个 Advice 的执行顺序可以通过 @Order 注解控制。不同切面间使用 @Order 或实现 Ordered 接口。
总结
Spring AOP 通过 JDK 动态代理和 CGLIB 两种机制在运行时创建代理对象,在方法执行前后织入横切逻辑。理解代理选择策略、Pointcut 表达式、Advice 执行链等核心概念,有助于正确使用 AOP 并规避自调用、代理失效等常见问题。
贡献者
更新日志
9f6c2-feat: organize wiki content and refresh site setup于