启动流程
启动类代码
1 |
|
对照上面的典型代码,这个两个元素分别是:
@SpringBootApplication
SpringApplication 以及 run() 方法
SpringApplication 这个类应该算是 SpringBoot 框架 的“创新”产物了,原始的 Spring中并没有这个类,SpringApplication 里面封装了一套 Spring 应用的启动流程,然而这对用户完全透明,因此我们上手 SpringBoot 时感觉简洁、轻量。
一般来说默认的 SpringApplication 执行流程已经可以满足大部分需求,但是 若用户想干预这个过程,则可以通过 SpringApplication 在流程某些地方开启的 扩展点 来完成对流程的扩展,典型的扩展方案那就是使用 set 方法。
我们来举一个栗子,把我们天天司空见惯的 SpringBoot 应用的启动类来拆解一下写出来:
1 |
|
这样一拆解后我们发现,我们也需要先构造 SpringApplication 类对象,然后调用该对象的 run() 方法。那么接下来就讲讲 SpringApplication 的构造过程 以及其 run() 方法的流程,搞清楚了这个,那么也就搞清楚了SpringBoot应用是如何运行起来的!
SpringApplication 实例的初始化
首先看下SpringApplication的构造方法:
1 | public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { |
详细过程如下:
- (1)推断应用的类型:根据你classpath 下是否能找到对应的class文件, 推断应用类型, 优先级依次是:REACTIVE 、NONE、SERVLET(默认)。
1 | static WebApplicationType deduceFromClasspath() { |
- (2)使用 SpringFactoriesLoader查找并加载 classpath下
META-INF/spring.factories
文件中所有可用的 ApplicationContextInitializer
1 | # Initializers |
- (3) 使用 SpringFactoriesLoader查找并加载 classpath下
META-INF/spring.factories
文件中的所有可用的 ApplicationListener
1 | # Application Listeners |
- (4) 推断并设置main方法的定义类
1 | private Class<?> deduceMainApplicationClass() { |
SpringApplication 的run()方法
先看代码:
1 | public ConfigurableApplicationContext run(String... args) { |
流程图如下:
自动装配原理
自动装配过程分析
自动装配原理得从 @SpringbootApplication
入手分析
@SpringbootApplication
包含了@SpringBootConfiguration
,@EnableAutoConfiguration
,@ComponentScan
1 | (ElementType.TYPE) |
@ComponentScan
如果没有指定扫描包,因此它默认扫描的是与该类同级的类或者同级包下的所有类;@SpringBootConfiguration
通过源码得知它是一个@Configuration
;@EnableAutoConfiguration
一旦加上此注解,那么将会开启自动装配功能,简单点讲,Spring会试图在你的classpath下找到所有配置的Bean然后进行装配。当然装配Bean时,会根据若干个@Conditional
定制规则来进行初始化;
1 | "deprecation") ( |
- 根据文档注释的说明它指点我们去看
EnableAutoConfigurationImportSelector
。但是该类在SpringBoot1.5.X版本已经过时了,因此我们看一下它的父类AutoConfigurationImportSelector
;
1 |
|
首先该类实现了DeferredImportSelector
接口,这个接口继承了ImportSelector
, 该接口主要是为了导入 @Configuration
的配置项,而 DeferredImportSelector
是延期导入,当所有的@Configuration
都处理过后才会执行;
- 回过头来我们看一下
AutoConfigurationImportSelector
的selectImport
方法, 该方法刚开始会先判断是否进行自动装配,而后会从META-INF/spring-autoconfigure-metadata.properties
读取元数据与元数据的相关属性,紧接着会调用getCandidateConfigurations
方法:
1 | protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, |
在这里又遇到SpringFactoryiesLoader
, 它会读取META-INF/spring.factories
下的EnableAutoConfiguration
的配置,紧接着在进行排除与过滤,进而得到需要装配的类。最后让所有配置在META-INF/spring.factories
下的AutoConfigurationImportListener
执行AutoConfigurationImportEvent
事件,代码如下:
1 | private void fireAutoConfigurationImportEvents(List<String> configurations, |
总结
1)自动装配还是利用了 SpringFactoriesLoader
来加载META-INF/spring.factoires
文件里所有配置的EnableAutoConfgruation
,它会经过exclude
和filter
等操作,最终确定要装配的类
2) 处理@Configuration
的核心还是ConfigurationClassPostProcessor
,这个类实现了BeanFactoryPostProcessor
, 因此当AbstractApplicationContext
执行refresh()
方法里的invokeBeanFactoryPostProcessors(beanFactory)
方法时会执行自动装配
自定义starter
Tomcat启动流程
EmbeddedWebServerFactoryCustomizerAutoConfiguration
内嵌web容器工厂自定义定制器装配类
org.springframework.context.support.AbstractApplicationContext#refresh
如何扫描自定义组件
Conditional注解
常见的注解解释:
@ConditionalOnBean
匹配给定的class类型或者Bean的名字是否在SpringBeanFactory中存在@ConditionalOnClass
匹配给定的class类型是否在类路径(classpath)中存在@ConditionalOnExpression
匹配给定springEL表达式的值返回true时@ConditionalOnJava
匹配JDK的版本,其中range属性是枚举类型有两个值可以选择- EQUAL_OR_NEWER 不小于
- OLDER_THAN 小于
value属性用于设置jdk版本
ConditionalOnMissingBean
spring上下文中不存在指定bean时ConditionalOnWebApplication
在web环境下创建