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

面试官问我: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
  • 源码

    java 复制代码
    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应用,定义如下代码:

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

请求链路详细分析

  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的动态决策,体现了多种设计模式的融合。通过详细分析请求链路和时序图,我们可以更深入理解其实现原理。希望这篇博客能帮你在面试中脱颖而出!

相关推荐
穿林鸟5 分钟前
Spring Boot项目信创国产化适配指南
java·spring boot·后端
褚翾澜6 分钟前
Haskell语言的NoSQL
开发语言·后端·golang
伏游13 分钟前
【BUG】生产环境死锁问题定位排查解决全过程
服务器·数据库·spring boot·后端·postgresql·bug
hycccccch1 小时前
Springcache+xxljob实现定时刷新缓存
java·后端·spring·缓存
你的人类朋友2 小时前
MQTT协议是用来做什么的?此协议常用的概念有哪些?
javascript·后端·node.js
于过2 小时前
Spring注解编程模型
java·后端
霍徵琅2 小时前
Groovy语言的物联网
开发语言·后端·golang
uhakadotcom2 小时前
阿里云Tea OpenAPI:简化Java与阿里云服务交互
后端·面试·github
申雪菱3 小时前
Scheme语言的数据挖掘
开发语言·后端·golang
程序员一诺3 小时前
【Flask开发】嘿马文学web完整flask项目第1篇:简介【附代码文档】
后端·python·flask·框架