springboot源码分析
目录
6.1 加载所有的META-INF/spring.factories文件中配置
6.2 将spring.factories文件中EnableAutoConfiguration配置类加入spring
7.2 DispatcherServlet的创建和加入web容器
8.2.1 ServerProperties注册到spring容器
(本篇测试、分析项目代码路径https://gitee.com/yejuan/springboot-learning.git 对应tag: c1)
1. 概述
springboot有大量的自动配置、条件注册bean使开发者很简单快捷地接入各种服务和组件,是优秀的敏捷开发工具。
- springboot中有大量的服务配置在META-INF/spring.factories文件中,使用时通过key找到相应的类再反射获取实例;
- springboot会自动过滤注册META-INF/spring.factories文件中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的class到spring容器
2. 基于springboot开发web项目
代码示例
jar包依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.version}</version>
</dependency>
spring boot启动类
@SpringBootApplication(scanBasePackages={"com.yej.learning"})
public class App
{
public static void main( String[] args )
{
/**
* App.class会设置到SpringApplication中的primarySources参数中
*/
SpringApplication.run(App.class);
}
}
通过上面的代码我们就搭建了一个web项目框架,下面我们分析下相关功能是怎么实现的
3. spring容器的创建
默认创建的spring容器为AnnotationConfigServletWebServerApplicationContext实例
- AnnotationConfigServletWebServerApplicationContext构造方法中会调用父类GenericApplicationContext构造方法new DefaultListableBeanFactory()创建beanFactory
- AnnotationConfigServletWebServerApplicationContext构造方法中会调用 new AnnotatedBeanDefinitionReader(this)方法进行将spring ioc支持组件加入spring 容器AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)
默认创建的spring容器为AnnotationConfigServletWebServerApplicationContext实例org.springframework.boot.SpringApplication#run(java.lang.String...)



spring容器中beanFactory的创建org.springframework.context.support.GenericApplicationContext#GenericApplicationContext()

spring ioc支持组件加入spring 容器AnnotationConfigServletWebServerApplicationContext#AnnotationConfigServletWebServerApplicationContext()



4. springboot启动类加入spring容器
spring boot启动时如示例通过SpringApplication.run(App.class)启动,会将run方法参数App类加入到spring容器,通过ConfigurationClassPostProcessor#processConfigBeanDefinitions解析App类时会扫描scanBasePackages配置的包路径下的所有bean并将其加入spring容器。
org.springframework.boot.SpringApplication#prepareContext




5. 基于条件配置bean
springboot中有大量的基于条件将bean加入spring容器的配置,我们来分析下。
5.1 什么时候进行条件判断
spring在扫描到有@Component修饰的类和有@Bean修饰的方法时在创建BeanDefinition注册到spring容器中前都会判断类和方法上是否有Conditional,Conditional不满足时会跳过相关bean的注册。下面示例了两处Conditional条件的判断,整个spring容器加载过程中Conditional条件的判断位置很多,就不一一列举了。org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

5.2 条件判断处理分析
获取类或方法上所有Conditional注解中配置的类,通过反射实例化得到Condition对象。依次调用Conditional实例的matches方法进行判断,如果有一个条件不满足则跳过,相关bean不加入spring容器。
org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

5.3 常见Conditional注解分析
springboot中有许许多多的基于条件的bean配置,通过自定义Conditional注解实现,常见的有@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnProperty等。自定义Conditional注解是通过引入@Conditional注解并且在@Conditional注解中配置自己的Condition接口实现的。
springboot中常见的自定义Conditional注解都实现了SpringBootCondition类在调用Conditional实例的matches方法进行判断时会调用到自定义配置的Condition实现类的getMatchOutcome方法进行条件判断。

org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)

5.3.1 @ConditionalOnBean
@ConditionalOnBean的自定义Condition实现类为OnBeanCondition,通过在@ConditionalOnBean注解中配置value、type、annotation、name属性判断spring容器中是否存在相关类型、有相关注解修饰和相关beanName的bean。
org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchingBeans



org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchOutcome
如果value、type、annotation、name 等属性配置存在未匹配到相关bean的情况,返回未匹配ConditionOutcome,反之匹配成功

5.3.2 @ConditionalOnClass
@ConditionalOnClass的自定义Condition实现类为OnClassCondition,获取ConditionalOnClass注解中配置的值,通过Class.forName过滤没有找到的class,存在未找到的class构建未匹配结果ConditionOutcome返回,所有的class都找到了构建匹配承购ConditionOutcome返回。org.springframework.boot.autoconfigure.condition.OnClassCondition#getMatchOutcome



5.3.3 @ConditionalOnProperty
@ConditionalOnProperty的自定义Condition实现类为OnPropertyCondition,遍历ConditionalOnProperty注解中配置的name、value,进行前缀拼接,判断资源属性(资源属性的加载可通过spring源码分析第三篇中介绍过通过@PropertySource注解加载资源配置文件)中是否配置相关key,如果未配置或者配置的value与指定值不一致则返回条件匹配不成功。
org.springframework.boot.autoconfigure.condition.OnPropertyCondition#getMatchOutcome


org.springframework.boot.autoconfigure.condition.OnPropertyCondition.Spec#collectProperties

6. 自动配置
6.1 加载所有的META-INF/spring.factories文件中配置
SpringApplication构造方法中会加载所有的META-INF/spring.factories文件中配置的键值对缓存到SpringFactoriesLoader#cache容器中对应的key为Launcher$AppClassLoader实例
org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)



6.2 将spring.factories文件中EnableAutoConfiguration配置类加入spring
SpringBootApplication注解中通过EnableAutoConfiguration注解import了AutoConfigurationImportSelector类,spring解析AutoConfigurationImportSelector时会获取META-INF/spring.factories文件中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的class, 获取到spring.factories中key为AutoConfigurationImportFilter配置的filter,filter再根据META-INF/spring-autoconfigure-metadata.properties中配置的ConditionalOnClass、ConditionalOnWebApplication、ConditionalOnBean过滤条件对EnableAutoConfiguration的class进行过滤,然后将过滤后的类加入spring容器org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

加载META-INF/spring-autoconfigure-metadata.properties配置文件org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#getAutoConfigurationMetadata


加载META-INF/spring.factories文件中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类。org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry


获取META-INF/spring.factories文件中配置的key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的类并示例化
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry




配置的AutoConfigurationImportFilter的类为OnBeanCondition,OnClassCondition,OnWebApplicationCondition

根据META-INF/spring-autoconfigure-metadata.properties中配置的ConditionalOnClass、ConditionalOnWebApplication、ConditionalOnBean过滤条件对configurations进行过滤org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry


过滤的规则在ConditionalOnClass、ConditionalOnWebApplication、ConditionalOnBean的match方法中有不同的实现org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#getOutcomes

spring.factories文件中EnableAutoConfiguration配置类经过过滤、解析后会加入ConfigurationClassParser的Map<ConfigurationClass, ConfigurationClass> configurationClasses容器,后续加入spring容器org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass


7. 内嵌Tomcat的创建和配置
7.1 创建Tomcat服务器
通过org.springframework.boot.autoconfigure.EnableAutoConfiguration引入配置类ServletWebServerFactoryAutoConfiguration,又通过ServletWebServerFactoryAutoConfiguration import了ServletWebServerFactoryConfiguration.EmbeddedTomcat.class因为spring-boot-starter-web pom 中加入的是tomcat服务器,所有又会通过@Bean将TomcatServletWebServerFactory加入spring容器,从spring中获取ServletWebServerFactory类型的bean拿到TomcatServletWebServerFactory,将创建Tomcat服务器


创建Tomcat服务器org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh


org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer

启动tomcat服务器,创建非守护进程阻止Tomcat服务器立马关闭org.springframework.boot.web.embedded.tomcat.TomcatWebServer#initialize

7.2 DispatcherServlet的创建和加入web容器
通过EnableAutoConfiguration引入了配置类DispatcherServletAutoConfiguration,DispatcherServletAutoConfiguration中通过@Bean将DispatcherServlet、DispatcherServletRegistrationBean加入了spring容器,DispatcherServlet实现了ApplicationContextAware接口在初始化方法调用时会将spring容器设置到DispatcherServlet中,spring boot中只有一个spring容器(首次请求web接口时会触发DispatcherServlet.init方法,进而调用到DispatcherServlet#initStrategies方法进行DispatcherServlet的初始化),在Tomcat启动时会调到Tomcat#start方法,进而调用到DispatcherServletRegistrationBean的onStartup方法将DispatcherServlet注册到web容器中。
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration


将spring容器设置到DispatcherServlet中org.springframework.web.servlet.FrameworkServlet#setApplicationContext

将DispatcherServlet加入web容器org.springframework.boot.web.servlet.RegistrationBean#onStartup



8. springboot属性加载
8.1 springboot默认属性加载文件
SpringApplication对象实例化时会获取所有的META-INF/spring.factories文件中配置的key为org.springframework.context.ApplicationListener的class通过反射拿到实例设置到listeners容器中,ConfigFileApplicationListener用于加载属性配置文件也是在这时实例化。SpringApplication.run方法会调用到prepareEnvironment方法进而调用到ConfigFileApplicationListener#postProcessEnvironment触发属性文件的加载。org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)


org.springframework.boot.context.config.ConfigFileApplicationListener#postProcessEnvironment

获取META-INF/spring.factories 文件中key为org.springframework.boot.env.PropertySourceLoader对应的value,反射获取相关类的实例,spring-boot\spring-boot-project\spring-boot\src\main\resources\META-INF\spring.factories文件中相关key配置的类为PropertiesPropertySourceLoader、YamlPropertySourceLoader。YamlPropertySourceLoader解析"yml", "yaml"后缀的属性文件,PropertiesPropertySourceLoader解析"properties", "xml"后缀的属性文件。解析完后将属性配置文件加入spring容器环境environment中资源属性中。
PropertiesPropertySourceLoader、YamlPropertySourceLoader属性文件加载器的实例化ConfigFileApplicationListener.Loader#Loader

加载属性文件ConfigFileApplicationListener.Loader#load()

默认加载DEFAULT_SEARCH_LOCATION常量配置的路径为 "classpath:/,classpath:/config/,file:./,file:./config/",默认加载文件名为常量DEFAULT_NAMES配置的application



通过PropertiesPropertySourceLoader、YamlPropertySourceLoader属性文件加载器的实例化进行属性文件的加载

解析完后将属性文件加入environment的属性资源中
org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load()


8.2 springboot配置bean的扫描和初始化
springboot web服务我们可以通过在application.yml进行下面的配置指定tomcat的端口,通过8.1我们知道springboot会将application.yml属性文件加载到springenvironment属性资源中。然后springboot会将属性资源的配置设置到org.springframework.boot.autoconfigure.web.ServerProperties bean中,我们分析下相关功能是怎么实现的。

8.2.1 ServerProperties注册到spring容器
ServerProperties是通过ServletWebServerFactoryAutoConfiguration bean上的@EnableConfigurationProperties(ServerProperties.class)注解加入到spring容器的。@EnableConfigurationProperties注解import了EnableConfigurationPropertiesRegistrar,EnableConfigurationPropertiesRegistrar实现了ImportBeanDefinitionRegistrar接口,在registerBeanDefinitions方法中:1.将ConfigurationPropertiesBindingPostProcessor、ConfigurationPropertiesBinder.Factory、ConfigurationPropertiesBinder、ConfigurationBeanFactoryMetadata注册到spring容器中 2.将EnableConfigurationProperties注解中配置的class注册到spring容器。

org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar#registerBeanDefinitions

将EnableConfigurationProperties注解中配置的class注册到spring容器

org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar#register(java.lang.Class<?>)

8.2.2 ServerProperties属性注入
上面分析到ServerProperties通过@EnableConfigurationProperties注解的解析加入到spring容器,ServerProperties的属性注入依赖于EnableConfigurationPropertiesRegistrar引入的bean,主要是ConfigurationPropertiesBindingPostProcessor,bean初始化之前会调用到ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization方法,处理有ConfigurationProperties注解修饰的bean,从spring environment的资源属性中获取配置的值设置到bean中。org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization

org.springframework.boot.context.properties.bind.Binder#bindDataObject

org.springframework.boot.context.properties.bind.JavaBeanBinder#bind(org.springframework.boot.context.properties.bind.DataObjectPropertyBinder, org.springframework.boot.context.properties.bind.JavaBeanBinder.Bean<T>, org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanSupplier<T>)



org.springframework.boot.context.properties.bind.Binder#findProperty

这里的context.getSources()获取到的属性资源是在Binder创建时获取到通过PropertySourcesDeducer#extractEnvironmentPropertySources()方法拿到spring容器environment中的属性资源设置到Binder中org.springframework.boot.context.properties.ConfigurationPropertiesBinder#bind


org.springframework.boot.context.properties.ConfigurationPropertiesBinder#ConfigurationPropertiesBinder



到这里我们就完成了springboot主要功能点的源码分析