编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。

前言

一直想抽空研读 Spring 源码,但真正着手时发现并非易事。Spring 发展多年,其规模已非一般开源框架可比,主要架构和流程错综复杂,难以轻易抓住要害。但可以肯定的是,IoC(控制反转)和 AOP(面向切面编程)是其根基,所有的功能扩展及对其它开源框架的支持均基于这两点构建。因此,掌握 Spring 源码的核心在于搞定 IoC 和 AOP 这两块内容。

IoC 容器核心

从原理上来说,IoC 非常简单,无非是从配置文件解析开始,到最后在内置容器中管理各个对象。但从 Spring IoC 源码来看,这是一个非常庞大的体系。因为 Spring 支持的特性太多,我们不太可能仔细阅读每一个细节,也不太可能在有限篇幅中表达所有细节,只能抓住关键生命周期中的关键步骤。

在我看来,IoC 最核心的是两个过程:IoC 容器初始化IoC 依赖注入。下面通过简单的图示来表述其中的关键过程。

相关延伸阅读:Spring 源码学习(二)------ AOP

AOP 实现机制

AOP 有一些特有的概念,如 advisoradvicepointcut 等,使用或配置起来略显复杂,让人感觉有些距离感。其实,它的实现就是一组标准设计模式的组合使用:FactoryProxyChain of Responsibility。只要搞清楚这几个设计模式,阅读 AOP 源码是比较容易的。

设计模式视角

首先看看 ProxyFactoryBean 这个类,这是 AOP 使用的入口。从 AOP 拿到的 bean object 就是通过 ProxyFactoryBean.getObject() 得到的。顺着这条线下去,会发现 AOP 就是通过 Proxy 模式 从实际要执行的 target 做了包装。

而 Proxy 还不止一套方案,通过 Factory 封装了两套 Proxy 实现方案:JDK 动态 ProxyCglib Proxy。之所以有两套实现,主要是因为 JDK 动态 Proxy 必须要 target 实现某个接口;如果不满足这个条件,就会用 Cglib 增强字节码的方式来实现 proxy。

代理模式应用

就拿 JDK Proxy 为例,Spring AOP 使用了标准的 JDK 提供的动态 Proxy 方案。我们先看看标准的动态 Proxy 是什么样子,看下面类图:

Client 通过 Proxy.newProxyInstance(classLoader, proxiedInterfaces, invocationHandler) 就能拿到 target 的 proxy object。在执行 target 的方法时,就会先执行到 DynamicProxy 中的 invoke 方法,从而实现代理包装。

基于这个道理来看 Spring AOP 的实现,实际上就是标准地基于这个方式来做的。Spring AOP 的所有“花招”都体现在 JdkDynamicAopProxy.invoke 中(当然在 Cglib 中是通过 callback 来做的,道理类似)。

责任链模式应用

通过看 JdkDynamicAopProxy.invoke 的源码会发现,Spring AOP 的各种逻辑是通过 Chain of Responsibility(责任链)模式 串起来的。先看看一个标准的 Chain of Responsibility 是什么样子,看下面的类图:

Chain of Responsibility 的关键在于 InvocationInterceptor 的配合,主要原则就两条:

  1. Invocation 需要维护 Interceptor 集合和游标,每次调用 invoke 时需要先调用游标所在的 Interceptor.invoke;如果游标已超过最后一个 Interceptor,则调用实际 target 的方法。
  2. Interceptorinvoke 中除了要执行自己的拦截逻辑,还要通过 Invocation.invoke 把调用传递下去。拦截的灵活性就体现在 Invocation.invoke 执行与否和执行的顺序。

以上逻辑通过时序图来看,如下图所示:

理解 Chain of Responsibility 后再来看 Spring AOP,JdkDynamicAopProxy.invoke 做的事情就是以上 Client 做的事情:

  1. 首先通过 this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass) 组装 interceptor chain。
  2. 然后 new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain) 得到 invocation。
  3. 最后通过 invocation.proceed() 启动责任链。

术语说明

  • advice 英 [əd'vaɪs] 美 [əd'vaɪs]

    • n. 建议;忠告;劝告;通知
  • advise 英 [əd'vaɪz] 美 [əd'vaɪz]

    • vt. 建议;劝告,忠告;通知;警告
    • vi. 建议;与…商量

在 Spring AOP 中,我们主要关注的是 advice(通知/建议),即切面在特定连接点执行的动作。

而 Spring AOP 的那些概念都体现在组装 interceptor chain 中,advisoradvicepointcut 无非就是帮助你描述如何对 Target 进行拦截。对这一块感兴趣的朋友可以好好读读里面的代码。另外,Spring AOP 提供了各种各样的 Interceptor 来实现各种形式的横切,具体做法可以详细看看各 Interceptor 的实现。

总结

综上所述,整个流程如下图所示:

总之,把握了 FactoryProxyChain of Responsibility 的运用,也就把握了 Spring AOP 的实现原理。道理虽然简单,但其中的精髓和原由还是值得我们继续深思的。

版本说明:本文内容基于早期 Spring 版本源码分析(参考链接时间约为 2011 年),部分类名或实现细节在新版本中可能有所调整,请以当前官方文档及最新源码为准。