大佬带你深入理解 Spring 依赖注入原理:bean 的注册及实例化
Spring 依赖注入原理分析
Spring 中关于依赖注入的代码实现非常丰富,涉及大量类和组件之间的协作与交互。从原理上讲,任何一个框架都存在一条核心执行流程,只要抓住这条主流程,我们就能把握框架的整体代码结构,Spring 也不例外。
无论采用何种依赖注入机制,前提都是 Spring IoC 容器正常启动。因此,IoC 容器初始化就是我们理解和把握依赖注入实现机制的前提。
本节结合 Bean 的生命周期,把 IoC 容器初始化过程梳理成两大步骤,即 Bean 的注册和 Bean 的实例化。这两个步骤就构成了一条代码主流程。
Bean 的注册
在使用 Spring 时,我们可以通过获取一个应用上下文(ApplicationContext)对象来操作各种 Bean,示例代码如下所示,相信你对这段代码不会陌生。
这里的 ApplicationContext 接口代表的就是一个 Spring IoC 容器,而在 Spring 中存在一大批 ApplicationContext 接口的实现类。如果使用基于注解的配置方式,就可以使用上述代码中的 AnnotationConfigApplicationContext 来初始化容器上下文对象。
在刚开始阅读 Spring 源码时,我建议你直接从 AnnotationConfigApplicationContext 的启动流程切入,这一流程位于它的构造函数中,如下所示:
这两个构造函数的作用很明确,一个是根据注解配置类注册 Bean,另一个则是根据包路径配置扫描 Bean。这里我们以 register()方法为例,来讨论 Bean 的注册过程,该方法如下所示。
这里依赖 AnnotatedBeanDefinitionReader 工具类来完成 Bean 的注册。
AnnotatedBean-DefinitionReader 会遍历所有传入的 annotatedClasses 注解类,然后通过如下所示的 doRegisterBean()方法完成注册。(由于该方法的代码较长,我们对重要逻辑添加了注释,对不重要的代码做了省略。)
这段代码包含 Bean 注册过程中的三个核心步骤,如下图所示:
首先,我们构建用来描述 Bean 实例信息的 BeanDefinition 对象,这需要将注解配置类信息转成 AnnotatedGenericBeanDefinition 数据结构,而 AnnotatedGenericBeanDefinition 就是一种 BeanDefinition,包含了 Bean 的构造函数参数、各种属性值以及所添加的注解信息。
然后,我们设置 BeanDefinition 属性,这一步骤完成了对 @Scope、@Primary、@Lazy 等注解的处理。最后,通过 registerBeanDefinition()方法完成 Bean 的注册,该方法内部通过 Listable-BeanFactory 的实现类 DefaultListableBeanFactory 将 Bean 定义信息注册到 Spring IoC 容器中。ListableBeanFactory 是 Spring 中常用的一个 BeanFactory,通过这个接口,我们可以一次获取多个 Bean。
Bean 的实例化
请注意,到现在为止,Spring IoC 容器对 Bean 的创建过程并没有完成,我们只是将 Bean 的定义加载到了容器中而已。但是容器本身可能已经存在这些 Bean 的定义,所以我们还需要调用 ApplicationContext 接口的抽象实现类 AbstractApplicationContext 中的 refresh()方法刷新容器,正如我们在前面看到的 AnnotationConfigApplicationContext 构造函数所执行的那样。
可以说,refresh()方法是整个 Spring 容器中最为核心的一个方法,值得我们详细讨论。但因为这里关注的是依赖注入,所以我们只列出 refresh()方法中与该主题相关的代码,如下所示:
可以看到,obtainFreshBeanFactory()方法完成 BeanDefinition 的注册并返回一个 Bean-Factory。对于 AnnotationConfigApplicationContext 而言,这一步实际上就是将 BeanDefinition 注册到 DefaultListableBeanFactory 而已,我们在前面已经介绍了这一步骤。
而 finishBeanFactoryInitialization()方法才是真正的完成 Bean 实例化的入口。在这个方法中,完成 Bean 的实例化代码实际上位于它的子类 DefaultListableBeanFactory 中,如下所示:
接下来,我们就进入到 getBean()方法了,这个方法可以从 BeanFactory 中获取一个 Bean,而 Bean 的初始化过程也被封装在这个方法中。在 getBean()方法中,我们一路跟踪代码会发现需要深入分析的实际上是如下所示的一个 createBean()抽象方法:
protected abstract Object createBean(String beanName,
RootBeanDefinition mbd, @Nullable Object[] args) throws
BeanCreationException;
请注意,在 Spring 中,实现这个抽象方法的唯一 BeanFactory 是 AbstractAutowireCap-ableBeanFactory。从命名上看,我们就可以联想到 @Autowired 注解。在 AbstractAutowire-CapableBeanFactory 中,真正完成 Bean 的创建是在 doCreateBean()方法中。doCreateBean()方法比较长,为了显示得更简洁,我们对代码做了大量裁剪之后得到如下所示的结构。
可以看到这里包含三个核心子方法,它们的名称和作用如图所示:
在以上三个步骤中,createBeanInstance()方法用于根据配置生成具体的 Bean,最终通过基于构造器的反射方法实现这一目标。请注意,执行完这一步之后,Bean 已经被创建了,但还不完整,因为属性还没有被注入。
接下来的 populateBean()方法就是用于实现属性的自动注入,包含 byName、byType 类型的自动装配,以及基于 @Autowired、@Value 注解的属性设值。执行完这一步之后,可以说 Bean 已经是完整的了。
而最后的 initializeBean()方法则更多是一种扩展性的实现机制,用于在 Bean 初始化完成之后执行一些定制化操作。
至此,针对整个 Bean 的注入过程(即 Bean 的注册和实例化),我们围绕核心流程做了剖析和总结。在这个过程中,比较容易碰到的一个问题就是陷入代码的细节而忽略了主体步骤。因此,如果想要跳出源码阅读的困境,快速掌握框架的实现原理,我们就必须从核心流程来看待框架。
接下来,我们将对 Spring 依赖注入中典型的循环依赖问题进行系统分析。
可以说,想要理解循环依赖问题以及对应的解决方案,掌握本节阐述的 Bean 的注册和实例化原理至关重要。
评论