面试官问我:Spring AOP的代理模式与实现原理深度剖析
在一次面试中,面试官问了我一个关于Spring AOP的问题:"你刚刚说了Spring AOP是基于CGLIB和JDK动态代理的。那你说,Spring AOP用的是哪一种代理模式?是如何实现这种选择的?体现了什么设计模式?如果让你来分析整个框架的请求链路,涉及到哪些类,哪些方法?你能否整合出一条从应用层到框架原理层的可复用方法论?如何感性地记忆整个处理流程?"
这次我将提供一个更深入的解答,包含时序图和代码细节。
Spring AOP用的是哪一种代理模式?
Spring AOP根据目标对象的特性动态选择代理模式:
- JDK动态代理 :
- 条件:目标对象实现了至少一个接口。
- 实现:基于
java.lang.reflect.Proxy
,生成一个实现目标接口的代理类。 - 限制:只能代理接口方法。
- CGLIB代理 :
- 条件:目标对象未实现接口,或配置
proxyTargetClass=true
。 - 实现:通过ASM字节码操作库,生成目标类的子类。
- 优势:可以代理非接口方法。
- 条件:目标对象未实现接口,或配置
默认策略:Spring优先选择JDK动态代理(更轻量,符合接口优先原则),但在必要时切换到CGLIB。
如何实现这种选择的?
代理选择的实现集中在org.springframework.aop.framework.DefaultAopProxyFactory
中:
-
关键方法 :
createAopProxy(AdvisedSupport config)
。 -
决策逻辑 :
- 检查
AdvisedSupport.isProxyTargetClass()
是否为true
(通过@EnableAspectJAutoProxy(proxyTargetClass = true)
或XML配置)。 - 检查
config.getTargetClass().getInterfaces()
是否为空。 - 如果
proxyTargetClass=true
或无接口,返回CglibAopProxy
;否则,返回JdkDynamicAopProxy
。
- 检查
-
源码 :
javapublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class"); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new CglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
体现了什么设计模式?
- 代理模式:通过代理对象控制目标对象访问。
- 工厂模式 :
DefaultAopProxyFactory
根据条件创建代理。 - 策略模式:动态选择JDK或CGLIB代理策略。
- 装饰器模式 :通过
MethodInterceptor
链增强方法调用。 - 责任链模式 :
ReflectiveMethodInvocation
中的拦截器链按顺序执行。
框架请求链路分析:类、方法与时序图
假设一个Spring MVC应用,定义如下代码:
java
@RestController
public class MyController {
@GetMapping("/test")
public String test() {
return "Hello, AOP!";
}
}
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.example.MyController.test(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Before");
Object result = pjp.proceed();
System.out.println("After");
return result;
}
}
请求链路详细分析
-
应用启动与AOP初始化:
AbstractAutowireCapableBeanFactory.createBean
:创建MyController
bean。AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization
:- 检查
MyController
是否匹配切面(AspectJExpressionPointcut
)。 - 调用
wrapIfNecessary
生成代理。
- 检查
ProxyFactory.createAopProxy
:调用DefaultAopProxyFactory.createAopProxy
。- 结果 :返回
JdkDynamicAopProxy
或CglibAopProxy
。
-
请求处理:
DispatcherServlet.doDispatch
:接收/test
请求。RequestMappingHandlerMapping.getHandler
:定位MyController.test()
。RequestMappingHandlerAdapter.invokeHandlerMethod
:- 创建
ServletInvocableHandlerMethod
。 - 调用
invokeAndHandle
,触发代理对象。
- 创建
-
代理执行:
- JDK动态代理 :
JdkDynamicAopProxy.invoke
:- 创建
ReflectiveMethodInvocation
。 - 调用
proceed()
执行拦截器链。
- 创建
- CGLIB代理 :
CglibAopProxy.DynamicAdvisedInterceptor.intercept
:- 同上,构造
ReflectiveMethodInvocation
。
- 同上,构造
ReflectiveMethodInvocation.proceed
:- 遍历
MethodInterceptor
列表(例如AspectJAroundAdvice
)。 - 执行
invoke
(MyAspect.around
)。 - 调用目标方法
MyController.test()
。
- 遍历
- JDK动态代理 :
时序图
以下是用Mermaid语法绘制的时序图,展示从请求到AOP执行的流程:
sequenceDiagram
participant U as User
participant DS as DispatcherServlet
participant HM as HandlerMapping
participant HA as HandlerAdapter
participant P as Proxy (JDK/CGLIB)
participant RMI as ReflectiveMethodInvocation
participant A as MyAspect
participant T as MyController
U->>DS: GET /test
DS->>HM: getHandler()
HM-->>DS: HandlerMethod
DS->>HA: invokeHandlerMethod()
HA->>P: invoke()/intercept()
P->>RMI: proceed()
RMI->>A: around()
A->>RMI: proceed()
RMI->>T: test()
T-->>RMI: "Hello, AOP!"
RMI-->>A: result
A-->>P: result
P-->>HA: result
HA-->>DS: result
DS-->>U: Response
可复用方法论:从应用层到框架原理层
- 确定入口 :从
DispatcherServlet
或bean初始化开始。 - 分析AOP初始化 :
- 关注
AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization
。 - 跟踪
ProxyFactory
和DefaultAopProxyFactory
。
- 关注
- 梳理代理调用 :
- JDK:
JdkDynamicAopProxy.invoke
。 - CGLIB:
CglibAopProxy.intercept
。
- JDK:
- 深入拦截器链 :研究
ReflectiveMethodInvocation
的proceed
逻辑。 - 验证与调试 :
- 设置断点:
invoke
、intercept
、proceed
。 - 日志:启用
spring.aop
DEBUG级别。
- 设置断点:
感性记忆处理流程
用"快递配送"比喻:
- 用户下单(请求) :寄快递(
DispatcherServlet
)。 - 物流中心(HandlerMapping):分配路线。
- 快递员(代理):选择运输方式(JDK/CGLIB)。
- 检查站(拦截器) :验货、加标签(
MyAspect
)。 - 送达(目标方法) :包裹到达(
MyController.test()
)。 - 回程(返回):送回确认单。
这个比喻让我轻松记住从请求到AOP执行的每一步。
总结
Spring AOP的代理选择基于DefaultAopProxyFactory
的动态决策,体现了多种设计模式的融合。通过详细分析请求链路和时序图,我们可以更深入理解其实现原理。希望这篇博客能帮你在面试中脱颖而出!