springboot自动装配

150 阅读4分钟

前言

springboot对java工程师来说,哪方面是它最大的优点呢?个人感觉是自动装配,为大家省去繁杂的配置,本着约定优于配置,按需自行配置等原则带来了极大便利。所以,如果一个人对自动装配都一点也不知道,那么基本可以断言,对springboot,他最多是会用,一定没有多深的理解。本篇想讲述清楚,springboot的自动装配到底是如何完成的。一起感受下前辈们对于设计模式、编码方法的使用以及编写框架的时候呈现的格局。

springboot的启动

springboot并不会一开始就执行自动装配,涉及一些环境准备的工作,因此,把自动装配和前面的准备工作分开来讲。 创建一个springboot的项目,application.yml写一下端口号

image.png

写一下启动类 image.png

最后创建一个类交给springboot,为了之后观察我们这个类如何被加载。

image.png

启动类run()方法打个断点,往下追踪:

image.png

咱们平时经常会获取项目中的ApplicationContext对象,进一步展开和BeanFactory或者Environment或者各种bean等等的交互。实际上获取的就是ConfigurableApplicationContext,再具体一些,在springboot web项目中获取到的上下文是ConfigurableApplicationContext 的实现类之一的AnnotationConfigServletWebServerApplicationContext

image.png

继续往下走: image.png

这里的setInitializers以及setListeners的执行均依靠反射进行。这里不展开,感兴趣的可以研究下。 image.png

image.png

image.png

所以在此处可以看出,咱们写的SpringApplication.run()实际上在执行过程中会先执行对SpringApplication的一个构造(构造中设置基础的initializers和listeners以及推断启动主类)。最终开始执行真正的run。 image.png

下面来看下这个run()方法,自动装配的秘密都在这里了。可以这么将,如果吃透此方法,那么整个springboot精华大半将被你拿下!

image.png

下面这个图中的createApplicationContext是怎么做的,一起看下: image.png

image.png

image.png

关注prepareContext方法

image.png

image.png

直接来关注getAllSources

image.png

image.png

primarySource中内容如下

image.png

追踪load资源如何进行: image.png

load方法中又通过BeanDefinitionLoader执行load image.png

load中执行遍历,当然,咱们这的等待被遍历的sources中就一个元素,且类型是calss.留一个小问题,如果这个循环由你来写,你会定一个var3来表示var2这个数组的长度吗,是不是会直接使用var2.length呢?

image.png

这个重载的load根据传递的Object的具体类型执行了不同的加载逻辑,显然,咱们会进入第一个if image.png

image.png 所以,来关注load((Class)source)

image.png

具体load class的时候,判断传递的class是否为一个组件,如果是,那么调用annotatedReader来注册这个组件。关注this.annotatedReader.register(new Class[]{source});方法

image.png

image.png

image.png

ok,开始兴奋了。下面这个方法的名字叫做doRegisterBean,真相即将揭晓

image.png

image.png

这个意义是重大的,因为,咱们的启动主类直到此处才真正地被springboot纳入管理中。

prepareContext执行完之后,来到refreshContext方法 image.png

image.png

image.png

image.png

ServletWebServerApplicationContext继承自AbstractApplicationContext,最终调用的是AbstractApplicationContext的refresh方法 image.png

image.png

调用PostProcessorRegistrationDelegate的静态方法invokeBeanFactoryPostProcessors image.png

PostProcessorRegistrationDelegate中invokeBeanFactoryPostProcessors这个方法一起来分析下。来到这里希望大家依旧保持清醒,目前比较重要的几个对象,咱们的上下文对象是继承自cofigurableApplicationContext 的AnnotationConfigServletWebServerApplicationContext。bean工厂是DefaultListableBeanFactory。截止到目前refreshContext阶段,咱们拥有的三个beanFactoryPostProcessors如下图所示:

image.png

image.png

上面if段执行完之后,最终获取到的postProcessor叫做org.springframework.context.annotation.internalConfigurationAnnotationProcessor,这个类被定义在哪里呢,定义在AnnotationConfigUtils中,搜索下AnnotationConfigUtils,你会发现这个internalConfigurationAnnotationProcessor和ConfigurationClassPostProcessor类的创建有密切的关系。

image.png image.png

image.png

当前我使用的springboot的版本是2.2.1.在PostProcessorRegistrationDelegate的 invokeBeanFactoryPostProcessors的75行这里,当前的currentRegistryProcessors就是ConfigurationClassPostProcessor,当前的registry包含org.springframework.context.annotation.internalConfigurationAnnotationProcessor

image.png

f7进入这个方法,看看具体做了什么

image.png

来到ConfigurationClassPostProcessor的processConfigBeanDefinitions( *)方法。 image.png

image.png

可以看到,candidateNames里面包含咱们的启动主类,以及一些其它的factory和processors。接下来肯定要做出解析: image.png

下面的do while循环对candidates做出解析,来看这个parse都做什么 image.png

image.png

image.png

image.png

现在,咱们来到了ConfigurationClassParser,这里的doProcessConfigurationClass方法要开始解析各种注解了。很好例界,目前的解析目标针对启动主类。我们最关心的是@Import注解怎么被解析的,直接找:

image.png

image.png

image.png

下面这个collectImports是一个递归方法,所以,相关的带有@Import的类都将被parse

image.png

最终执行完的结果是,找到了两个@Import注解。分别位于

image.png

image.png

image.png

其实到这里自动装配已经基本完成了,下面需要解决的是,这些autoConfiguration是怎么被加载的呢?这个留在后面思考