Spring AOP(面向切面编程)的实现基于动态代理技术,通过在运行时动态生成代理对象来拦截目标方法,并在方法执行前后插入切面逻辑(如日志、事务管理等)。其核心实现机制如下:
1. 核心实现技术
Spring AOP 主要依赖两种动态代理技术:
- JDK 动态代理:基于接口的代理,要求目标对象实现至少一个接口。
- CGLIB 动态代理:基于子类的代理,通过生成目标类的子类来拦截方法(适用于无接口的类)。
Spring 默认优先使用 JDK 动态代理,若目标类未实现接口,则自动切换到 CGLIB。
2. 核心组件与流程
(1) 切面(Aspect)与通知(Advice)
- 切面 :通过
@Aspect
注解定义的类,包含多个通知方法。 - 通知类型 :
@Before
:方法执行前执行。@After
:方法执行后执行(无论是否异常)。@AfterReturning
:方法正常返回后执行。@AfterThrowing
:方法抛出异常后执行。@Around
:包裹目标方法,可自定义执行逻辑。
(2) 切入点(Pointcut)
- 通过表达式(如
execution(* com.example.service.*.*(..))
)定义哪些方法需要被拦截。 - 使用
@Pointcut
注解定义可重用的切入点。
(3) 代理对象的生成
- 解析切面 :Spring 容器启动时,解析所有
@Aspect
注解的类,提取通知和切入点。 - 创建 Advisor :将通知和切入点组合为
Advisor
对象(一个 Advisor 对应一个通知和一个切入点)。 - 生成代理 :
- 对目标对象,根据是否有接口选择 JDK 或 CGLIB 代理。
- 代理对象会拦截目标方法,根据切入点匹配决定是否执行通知逻辑。
(4) 拦截器链(Interceptor Chain)
- 当目标方法被调用时,代理对象会将所有匹配的
Advisor
转换为拦截器(MethodInterceptor
)。 - 拦截器按顺序形成链式调用(责任链模式),依次执行通知逻辑,最终调用目标方法。
3. 示例流程(@Around 为例)
java
@Aspect
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method: " + joinPoint.getSignature());
Object result = joinPoint.proceed(); // 调用目标方法
System.out.println("After method: " + joinPoint.getSignature());
return result;
}
}
- Spring 创建目标 Service 的代理对象。
- 调用代理对象的方法时,触发
MethodInterceptor
(即logMethod
)。 - 拦截器执行
@Around
逻辑,控制目标方法的执行。
4. 实现细节
-
JDK 动态代理 :通过
java.lang.reflect.Proxy
和InvocationHandler
实现。javapublic class JdkProxy implements InvocationHandler { private Object target; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 执行通知逻辑 return method.invoke(target, args); } }
-
CGLIB 动态代理 :通过
Enhancer
和MethodInterceptor
生成子类。javapublic class CglibProxy implements MethodInterceptor { public Object getProxy(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 执行通知逻辑 return proxy.invokeSuper(obj, args); } }
5. 局限性与注意事项
- 局限性 :
- 只能拦截 Spring 容器管理的 Bean 的方法调用。
- 无法拦截私有方法、静态方法或非 Spring 管理的对象。
- 性能:CGLIB 生成代理的速度较慢,但调用效率与 JDK 代理接近。
- 与 AspectJ 对比:Spring AOP 更轻量但功能较弱;AspectJ 提供更强大的切面功能(如字段拦截、编译时织入)。
6.1 核心流程的时序图(使用 Mermaid 语法),
展示了从 Spring 容器启动到方法拦截的完整过程:
sequenceDiagram
participant Spring容器
participant Aspect解析器
participant ProxyFactory
participant ProxyObject
participant InterceptorChain
participant TargetObject
participant Client
Note over Spring容器: 1. Spring 容器启动阶段
Spring容器->>Aspect解析器: 扫描所有@Aspect类
Aspect解析器->>Aspect解析器: 解析通知和切入点
Aspect解析器->>Spring容器: 生成Advisor列表
Note over Spring容器: 2. Bean 初始化阶段
Spring容器->>ProxyFactory: 创建目标Bean的代理
alt 目标类有接口
ProxyFactory-->>ProxyObject: JDK动态代理
else 目标类无接口
ProxyFactory-->>ProxyObject: CGLIB动态代理
end
Spring容器->>Client: 注入代理对象(而非原始对象)
Note over Client: 3. 方法调用阶段
Client->>ProxyObject: 调用目标方法(如userService.save())
activate ProxyObject
ProxyObject->>InterceptorChain: 触发拦截器链
activate InterceptorChain
loop 按顺序执行拦截器
InterceptorChain->>@Around Advice: 执行前置逻辑
@Around Advice-->>InterceptorChain: 是否继续?
end
InterceptorChain->>TargetObject: 调用原始目标方法
activate TargetObject
TargetObject-->>InterceptorChain: 方法返回结果
deactivate TargetObject
loop 按逆序执行后置拦截器
InterceptorChain->>@AfterReturning Advice: 执行后置逻辑
@AfterReturning Advice-->>InterceptorChain: 处理结果
end
InterceptorChain-->>ProxyObject: 返回最终结果
deactivate InterceptorChain
ProxyObject-->>Client: 返回结果
deactivate ProxyObject
6.2 时序图关键步骤说明
阶段 1:Spring 容器启动
- 扫描切面 :Spring 容器扫描所有
@Aspect
注解的类。 - 解析通知和切入点 :提取
@Before
、@Around
等通知方法,以及@Pointcut
定义的切入点。 - 生成 Advisor :将通知和切入点组合为
Advisor
(每个 Advisor 对应一个切入点和一个通知)。
阶段 2:Bean 初始化
- 创建代理对象 :
- 如果目标类实现了接口,使用 JDK 动态代理生成代理。
- 如果目标类无接口,使用 CGLIB 生成子类代理。
- 依赖注入:向客户端(如 Controller)注入代理对象,而非原始对象。
阶段 3:方法调用
- 代理对象拦截 :客户端调用代理对象的方法(如
userService.save()
)。 - 拦截器链执行 :
- 前置处理 :按顺序执行
@Around
的前置逻辑和@Before
。 - 目标方法调用:通过反射调用原始目标方法。
- 后置处理 :逆序执行
@Around
的后置逻辑、@AfterReturning
、@After
等。
- 前置处理 :按顺序执行
- 返回结果:最终结果通过代理对象返回给客户端。
关键设计模式
- 动态代理模式:通过代理对象拦截方法调用。
- 责任链模式:拦截器链按顺序执行多个通知。
- 适配器模式 :将
Advisor
适配为MethodInterceptor
。
常见问题
-
为什么私有方法无法被 AOP 拦截?
动态代理只能拦截公有方法,私有方法不会被代理对象重写。
-
如何强制使用 CGLIB 代理?
在
@EnableAspectJAutoProxy
中设置proxyTargetClass = true
。 -
AOP 失效的场景有哪些?
- 同类方法内部调用(绕过代理,直接调用原始方法)。
- 静态方法或非 Spring 管理的对象。
通过时序图可以清晰看到 Spring AOP 从代理对象的生成到拦截器链执行的全流程,理解其如何将横切逻辑(如日志、事务)与业务代码解耦。
总结
Spring AOP 通过动态代理在运行时生成代理对象,结合切面、通知和切入点的定义,实现了非侵入式的横切关注点(如日志、事务)的模块化。其灵活性和易用性使得它成为 Spring 生态中不可或缺的组件。