面试官问我:Spring AOP的代理模式与实现原理深度剖析

259 阅读3分钟

面试官问我:Spring AOP的代理模式与实现原理深度剖析

在一次面试中,面试官问了我一个关于Spring AOP的问题:“你刚刚说了Spring AOP是基于CGLIB和JDK动态代理的。那你说,Spring AOP用的是哪一种代理模式?是如何实现这种选择的?体现了什么设计模式?如果让你来分析整个框架的请求链路,涉及到哪些类,哪些方法?你能否整合出一条从应用层到框架原理层的可复用方法论?如何感性地记忆整个处理流程?”
这次我将提供一个更深入的解答,包含时序图和代码细节。

Spring AOP用的是哪一种代理模式?

Spring AOP根据目标对象的特性动态选择代理模式:

  1. JDK动态代理
    • 条件:目标对象实现了至少一个接口。
    • 实现:基于java.lang.reflect.Proxy,生成一个实现目标接口的代理类。
    • 限制:只能代理接口方法。
  2. CGLIB代理
    • 条件:目标对象未实现接口,或配置proxyTargetClass=true
    • 实现:通过ASM字节码操作库,生成目标类的子类。
    • 优势:可以代理非接口方法。

默认策略:Spring优先选择JDK动态代理(更轻量,符合接口优先原则),但在必要时切换到CGLIB。

如何实现这种选择的?

代理选择的实现集中在org.springframework.aop.framework.DefaultAopProxyFactory中:

  • 关键方法createAopProxy(AdvisedSupport config)
  • 决策逻辑
    1. 检查AdvisedSupport.isProxyTargetClass()是否为true(通过@EnableAspectJAutoProxy(proxyTargetClass = true)或XML配置)。
    2. 检查config.getTargetClass().getInterfaces()是否为空。
    3. 如果proxyTargetClass=true或无接口,返回CglibAopProxy;否则,返回JdkDynamicAopProxy
  • 源码
    public 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);
        }
    }
    

体现了什么设计模式?

  1. 代理模式:通过代理对象控制目标对象访问。
  2. 工厂模式DefaultAopProxyFactory根据条件创建代理。
  3. 策略模式:动态选择JDK或CGLIB代理策略。
  4. 装饰器模式:通过MethodInterceptor链增强方法调用。
  5. 责任链模式ReflectiveMethodInvocation中的拦截器链按顺序执行。

框架请求链路分析:类、方法与时序图

假设一个Spring MVC应用,定义如下代码:

@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;
    }
}

请求链路详细分析

  1. 应用启动与AOP初始化

    • AbstractAutowireCapableBeanFactory.createBean:创建MyController bean。
    • AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization
      • 检查MyController是否匹配切面(AspectJExpressionPointcut)。
      • 调用wrapIfNecessary生成代理。
    • ProxyFactory.createAopProxy:调用DefaultAopProxyFactory.createAopProxy
    • 结果:返回JdkDynamicAopProxyCglibAopProxy
  2. 请求处理

    • DispatcherServlet.doDispatch:接收/test请求。
    • RequestMappingHandlerMapping.getHandler:定位MyController.test()
    • RequestMappingHandlerAdapter.invokeHandlerMethod
      • 创建ServletInvocableHandlerMethod
      • 调用invokeAndHandle,触发代理对象。
  3. 代理执行

    • JDK动态代理JdkDynamicAopProxy.invoke
      • 创建ReflectiveMethodInvocation
      • 调用proceed()执行拦截器链。
    • CGLIB代理CglibAopProxy.DynamicAdvisedInterceptor.intercept
      • 同上,构造ReflectiveMethodInvocation
    • ReflectiveMethodInvocation.proceed
      • 遍历MethodInterceptor列表(例如AspectJAroundAdvice)。
      • 执行invokeMyAspect.around)。
      • 调用目标方法MyController.test()

时序图

以下是用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

可复用方法论:从应用层到框架原理层

  1. 确定入口:从DispatcherServlet或bean初始化开始。
  2. 分析AOP初始化
    • 关注AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInitialization
    • 跟踪ProxyFactoryDefaultAopProxyFactory
  3. 梳理代理调用
    • JDK:JdkDynamicAopProxy.invoke
    • CGLIB:CglibAopProxy.intercept
  4. 深入拦截器链:研究ReflectiveMethodInvocationproceed逻辑。
  5. 验证与调试
    • 设置断点:invokeinterceptproceed
    • 日志:启用spring.aop DEBUG级别。

感性记忆处理流程

用“快递配送”比喻:

  • 用户下单(请求):寄快递(DispatcherServlet)。
  • 物流中心(HandlerMapping):分配路线。
  • 快递员(代理):选择运输方式(JDK/CGLIB)。
  • 检查站(拦截器):验货、加标签(MyAspect)。
  • 送达(目标方法):包裹到达(MyController.test())。
  • 回程(返回):送回确认单。

这个比喻让我轻松记住从请求到AOP执行的每一步。

总结

Spring AOP的代理选择基于DefaultAopProxyFactory的动态决策,体现了多种设计模式的融合。通过详细分析请求链路和时序图,我们可以更深入理解其实现原理。希望这篇博客能帮你在面试中脱颖而出!