前言
springboot对java工程师来说,哪方面是它最大的优点呢?个人感觉是自动装配,为大家省去繁杂的配置,本着约定优于配置,按需自行配置等原则带来了极大便利。所以,如果一个人对自动装配都一点也不知道,那么基本可以断言,对springboot,他最多是会用,一定没有多深的理解。本篇想讲述清楚,springboot的自动装配到底是如何完成的。一起感受下前辈们对于设计模式、编码方法的使用以及编写框架的时候呈现的格局。
springboot的启动
springboot并不会一开始就执行自动装配,涉及一些环境准备的工作,因此,把自动装配和前面的准备工作分开来讲。 创建一个springboot的项目,application.yml写一下端口号
写一下启动类
最后创建一个类交给springboot,为了之后观察我们这个类如何被加载。
启动类run()方法打个断点,往下追踪:
咱们平时经常会获取项目中的ApplicationContext对象,进一步展开和BeanFactory或者Environment或者各种bean等等的交互。实际上获取的就是ConfigurableApplicationContext,再具体一些,在springboot web项目中获取到的上下文是ConfigurableApplicationContext 的实现类之一的AnnotationConfigServletWebServerApplicationContext
继续往下走:
这里的setInitializers以及setListeners的执行均依靠反射进行。这里不展开,感兴趣的可以研究下。
所以在此处可以看出,咱们写的SpringApplication.run()实际上在执行过程中会先执行对SpringApplication的一个构造(构造中设置基础的initializers和listeners以及推断启动主类)。最终开始执行真正的run。
下面来看下这个run()方法,自动装配的秘密都在这里了。可以这么将,如果吃透此方法,那么整个springboot精华大半将被你拿下!
下面这个图中的createApplicationContext是怎么做的,一起看下:
关注prepareContext方法
直接来关注getAllSources
primarySource中内容如下
追踪load资源如何进行:
load方法中又通过BeanDefinitionLoader执行load
load中执行遍历,当然,咱们这的等待被遍历的sources中就一个元素,且类型是calss.留一个小问题,如果这个循环由你来写,你会定一个var3来表示var2这个数组的长度吗,是不是会直接使用var2.length呢?
这个重载的load根据传递的Object的具体类型执行了不同的加载逻辑,显然,咱们会进入第一个if
所以,来关注load((Class)source)
具体load class的时候,判断传递的class是否为一个组件,如果是,那么调用annotatedReader来注册这个组件。关注this.annotatedReader.register(new Class[]{source});方法
ok,开始兴奋了。下面这个方法的名字叫做doRegisterBean,真相即将揭晓
这个意义是重大的,因为,咱们的启动主类直到此处才真正地被springboot纳入管理中。
prepareContext执行完之后,来到refreshContext方法
ServletWebServerApplicationContext继承自AbstractApplicationContext,最终调用的是AbstractApplicationContext的refresh方法
调用PostProcessorRegistrationDelegate的静态方法invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate中invokeBeanFactoryPostProcessors这个方法一起来分析下。来到这里希望大家依旧保持清醒,目前比较重要的几个对象,咱们的上下文对象是继承自cofigurableApplicationContext 的AnnotationConfigServletWebServerApplicationContext。bean工厂是DefaultListableBeanFactory。截止到目前refreshContext阶段,咱们拥有的三个beanFactoryPostProcessors如下图所示:
上面if段执行完之后,最终获取到的postProcessor叫做org.springframework.context.annotation.internalConfigurationAnnotationProcessor,这个类被定义在哪里呢,定义在AnnotationConfigUtils中,搜索下AnnotationConfigUtils,你会发现这个internalConfigurationAnnotationProcessor和ConfigurationClassPostProcessor类的创建有密切的关系。
当前我使用的springboot的版本是2.2.1.在PostProcessorRegistrationDelegate的 invokeBeanFactoryPostProcessors的75行这里,当前的currentRegistryProcessors就是ConfigurationClassPostProcessor,当前的registry包含org.springframework.context.annotation.internalConfigurationAnnotationProcessor
f7进入这个方法,看看具体做了什么
来到ConfigurationClassPostProcessor的processConfigBeanDefinitions( *)方法。
可以看到,candidateNames里面包含咱们的启动主类,以及一些其它的factory和processors。接下来肯定要做出解析:
下面的do while循环对candidates做出解析,来看这个parse都做什么
现在,咱们来到了ConfigurationClassParser,这里的doProcessConfigurationClass方法要开始解析各种注解了。很好例界,目前的解析目标针对启动主类。我们最关心的是@Import注解怎么被解析的,直接找:
下面这个collectImports是一个递归方法,所以,相关的带有@Import的类都将被parse
最终执行完的结果是,找到了两个@Import注解。分别位于
其实到这里自动装配已经基本完成了,下面需要解决的是,这些autoConfiguration是怎么被加载的呢?这个留在后面思考