Spring AOP
Spring AOP 基于代理模式实现,包含 JDK 动态代理和 CGLIB 两种方式。AOP(面向切面编程)将横切关注点(日志、事务、安全)与业务逻辑分离,减少代码重复。
提示
适用场景 日志记录、性能监控、事务管理、权限控制、缓存处理。
核心概念
| 概念 | 说明 |
|---|---|
| Pointcut(切点) | 定义哪些方法需要被拦截 |
| Advice(通知) | 切点执行的增强动作(前置、后置、环绕等) |
| Aspect(切面) | 切点 + 通知的组合 |
| Advisor(通知器) | Spring 内部的切面表示 |
| Weaving(织入) | 将切面应用到目标对象的过程 |
AOP 标签解析
AopNamespaceHandler
@Override
public void init() {
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
}aop:config 解析
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
configureAutoProxyCreator(parserContext, element);
for (Element elt : DomUtils.getChildElements(element)) {
if (POINTCUT.equals(localName)) parsePointcut(elt, parserContext);
else if (ADVISOR.equals(localName)) parseAdvisor(elt, parserContext);
else if (ASPECT.equals(localName)) parseAspect(elt, parserContext);
}
}代理类生成
入口:AnnotationAwareAspectJAutoProxyCreator.postProcessBeforeInstantiation
是否应该代理?
├── 基础类检测(跳过 Advice、Pointcut 等)
└── Advisor 寻找 → 适用性检测 → 结果缓存// postProcessAfterInitialization
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
return createProxy(bean.getClass(), beanName, specificInterceptors,
new SingletonTargetSource(bean));
}
return bean;
}代理方式选择
proxy-target-class 属性
| 值 | 代理方式 |
|---|---|
| false(默认) | 只为接口生成代理(JDK 动态代理) |
| true | 强制使用 CGLIB 生成子类代理 |
JDK 动态代理 vs CGLIB
| 维度 | JDK 动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 实现接口 | 继承类 |
| 代理类 | Proxy 子类 | 被代理类子类 |
| 限制 | 必须实现接口 | 不能代理 final |
| 性能 | 反射调用,较慢 | 优于 JDK |
| 适用 | 业务接口实现类 | 类(无接口) |
// JDK 动态代理
public class JdkProxy implements InvocationHandler {
private Object target;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置处理");
Object result = method.invoke(target, args);
System.out.println("后置处理");
return result;
}
}
// CGLIB 子类代理
public class CglibProxy extends TargetClass {
@Override
public void method() {
System.out.println("前置处理");
super.method();
System.out.println("后置处理");
}
}aop:scoped-proxy
用于代理作用域 bean(如 request、session):
<bean id="shoppingCart" class="example.ShoppingCart" scope="session">
<aop:scoped-proxy/>
</bean>aop:aspectj-autoproxy
基于 @AspectJ 注解的自动代理:
<aop:aspectj-autoproxy/>@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.service..*.*(..))")
public void before(JoinPoint point) {
System.out.println("方法执行前");
}
@Around("execution(* com.example.service..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
System.out.println("执行时间: " + (System.currentTimeMillis() - start));
return result;
}
}通知类型
| 类型 | 注解 | 说明 |
|---|---|---|
| 前置通知 | @Before | 方法执行前 |
| 后置通知 | @AfterReturning | 方法正常返回后 |
| 异常通知 | @AfterThrowing | 方法抛出异常 |
| 最终通知 | @After | 无论是否异常 |
| 环绕通知 | @Around | 方法前后都执行 |
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 同类内部调用不生效 | this 调用绕过代理 | 注入自身或使用 AopContext.currentProxy() |
| 注解在 private 方法上 | 代理无法拦截 private 方法 | 改为 public 方法 |
| 异常被 catch 吞掉 | 事务无法感知异常 | 重新抛出异常或手动回滚 |