Springboot源码分析启动流程自动配置详解
上一篇:SpringBoot源码分析-启动流程-准备ApplicationContext
概要
阅读本篇博客需要对Spring源码有一定的阅读经验,比如Spring先将要实例化类信息封装为一个BeanDefinition,然后根据BeanDefinition实例化Bean。Spring提供了那些方式可以导入类并封装为BeanDefinition,在封装过程中可以通过什么扩展点进行干预。
阅读前知识准备
在这里补充点Spring核心类ConfigurationClassPostProcessor以及ConfigurationClassParse的相关知识。
ConfigurationClassPostProcessor拿到被@Configuration修饰的配置类,使用ConfigurationClassParser.parse()->processConfigurationClass()->doProcessConfigurationClassprocessImports()方法扫描配置类或者配置类方法上面的注解,比如@ComponentScan @Import @PropertySource @ImportSource @Bean。对于SpringBoot的自动配置,@Import是关键
ConfigurationClassParser.parse()在处理@Import时分为了4种情况
@Import(普通类)
立即使用processConfigurationClass()->doProcessConfigurationClass()->processImports()方式按照@Configuration修饰的配置类一样处理该“普通类”
@Import(Class implements ImportSelector)
立即调用processImports()方法处理导入的类。虽然它也是立即,但是相比前者,这种方式只能处理当前类的@Import注解,而前者可以处理@ComponentScan @PropertySource @ImportSource @Bean等注解
@Import(Class implements DeferredImportSelector)
完成了对主配置类的处理之后,使用DeferredImportSelectorGroupingHandler按组处理,最终还是循环所有配置类并调用processImports()方法
@Import(Class implements ImportBeanDefinitionRegistrar)
这四种情况可以无限嵌套下去,厉害吧。
其实在doProcessConfigurationClass中,还支持嵌套类配置,还支持当前类父类配置。。。感觉你能想到的地方都可以配置。
如果不是很看得懂的话,建议先把Spring的源码看一看。。。接下来把重点放到SpringBoot如何将找到要自动配置类列表上
自动配置
从@SpringBootAppication注解中找两个@Import
图中的两个类从哪里来的喃,从启动类的@SpringBootApplication注解中一步步找
@SpringBootConfiguration@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
再点开@EnableAutoConfiguration
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
已经找到了一个@Import,再继续点开AutoConfigurationPackage
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
两个@Import都找到了
AutoConfigurationImportSelector
看看AutoConfigurationImportSelector的定义
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
关键信息:DeferredImportSelector,所以在看源码应该看它重写的方法:
getImportGroup
selectImports
关于两个方法更详细的讲解在后面给出
AutoConfigurationPackages.Registrar
看看它的定义
public abstract class AutoConfigurationPackages {static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
}
}
关键信息:ImportBeanDefinitionRegistrar,所以在看源码时应该看它重写的方法:
registerBeanDefinitions
看到这里还很抽象,不过SpringBoot就是用它们两完成了自动配置
在Spring源码中看看两者是如何添加到对应的待处理集合中的
AutoConfigurationPackages.Registrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
/**
* 将ImportBeanDefinitionRegistrar添加到configClass,而这个configClass会添加到
* ConfigurationClassParse.configurationClasses Map中,当主配置类完成解析之后
* 会循环所有的configurationClasses,再对其进行处理
*/
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
AutoConfigurationImportSelector
for (SourceClass candidate : importCandidates) {if (candidate.isAssignable(ImportSelector.class)) {
。。。。省略部分代码。。。。
if (selector instanceof DeferredImportSelector) {
/**
* 优先于ImportBeanDefinitionRegistrar处理
*/
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
。。。。省略部分代码。。。。
}
。。。。省略部分代码。。。。
}
到此为止,两个类都已经处理待处理状态了,那么谁先被处理喃,在代码注释中写了,DeferredImportSelector会优先于ImportBeanDefinitionRegistrar处理,所以先看AutoConfigurationImportSelector
AutoConfigurationImportSelector
打开整个类看,我想应该都是懵逼的吧,前面提到应该重点关注被重写的方法,因为被重写的方法。
在两个被重写的方法中打上断点跑一下
断点首先停在了
public Class<? extends Group> getImportGroup() {return AutoConfigurationGroup.class;
}
看看栈帧中的内容
public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {
。。。。省略部分代码。。。。。
/**
* 处理@Configuration修饰的配置类
*/
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
。。。。省略部分代码。。。。。
}
/** 处理DeferredImportSelector */
this.deferredImportSelectorHandler.process();
}
从这里证明了DeferredImportSelector是在处理完主配置类之后才会被处理的结论
从这里往下一步步DEBUG
ConfigurationClassParser.processGroupImports
public void processGroupImports() {for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
/**
* grouping.getImports() 决定了那些自动配置的类
* grouping.getImports().forEach 循环调用processImports完成对应BeanDefinition的注册
*/
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
。。。省略部分代码。。。
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
。。。省略部分代码。。。
});
}
}
往下看grouping.getImports()
public Iterable<Group.Entry> getImports() {for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
/**
* 调用自定义的DeferredImportSelector.Group的process方法
*/
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
继续往下AutoConfigurationGroup#process
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
/**
* 1.getAutoConfigurationMetadata():将spring-boot-autoconfigure模块中的classes中的META-INFO/spring-autoconfigure-metadata.properties中的配置加载到内存中
* 2.getAutoConfigurationEntry():将spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration配置项进行过滤返回
* 合格的自动配置项
*/
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
/**
* 从jar中spring.factories读取所有org.springframework.boot.autoconfigure.EnableAutoConfiguration配置项
* 方法里面写死读取EnableAutoConfiguration
*/
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//排重
configurations = removeDuplicates(configurations);
/**
* 找到需要排除的自动配置类
* 1.使用EnableAutoConfiguration.exclude 排除指定的Class
* 2.使用EnableAutoConfiguration.excludeName 排除指定ClassName的Class
* 3.使用spring.autoconfigure.exclude配置要排除的Class
*/
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//校验要排除的自动配置类是否合法(在类路径下是否存在,是否在spring.factories中已经配置了)
checkExcludedClasses(configurations, exclusions);
//从候选者当中删除要排除掉的自动配置类
configurations.removeAll(exclusions);
/**
* 使用spring.factories中org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的配置项(Import过滤器),比如:
* OnBeanConditio、OnClassCondition、OnWebApplicationCondition
* 将使用这些过滤器把configurations与autoConfigurationMetadata进行过滤,保留有效的自动配置类
*/
configurations = filter(configurations, autoConfigurationMetadata);
/**
* 从spring.factories中org.springframework.boot.autoconfigure.AutoConfigurationImportListener的配置项,默认只有一个:
* ConditionEvaluationReportAutoConfigurationImportListener
* 然后会生成一个AutoConfigurationImportEvent事件并传递给ConditionEvaluationReportAutoConfigurationImportListener
*/
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
该方法中每个方法的注释就不贴出来了,当然下面这个方法必须贴出来,太重要了,也是SpringBoot相当厉害的一部分,我甚至感觉可以单独拿一篇来说
configurations = filter(configurations, autoConfigurationMetadata);
里面到底如何工作的,注释应该写得很清楚了
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
/**
* 从spring.factories中读取的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter组的配置项,循环这些Filter,调用它们的getOutcomes方法,
* 循环configurations,调用autoConfigurationMetadata.getProperty(ClassName+每个Filter固定参数名(OnBeanCondition的固定参数名:ConditionalOnBean))。
*
*
* 默认有:OnBeanCondition OnClassCondition OnWebApplicationCondition 三个分别拿一个进行分析:
* ——OnBeanCondition:getOutcomes,固定参数名:ConditionalOnBean
* 会在spring-autoconfigure-metadata中找"org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.ConditionalOnBean=org.springframework.cache.interceptor.CacheAspectSupport"
* 如果找到,则匹配成功,返回的candidates就会包含它
* ——OnClassCondition:getOutcomes,固定参数名:ConditionalOnClass
* 会在spring-autoconfigure-metadata中找"org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration.ConditionalOnClass=javax.jms.Message,org.springframework.jms.core.JmsTemplate"
* 如果找到,则匹配成功,返回的candidates就会包含它
* ——OnWebApplicationCondition:getOutcomes,固定参数名:ConditionalOnWebApplication
* 会在spring-autoconfigure-metadata中找"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnWebApplication=SERVLET"
* 如果找到,则匹配成功,返回的candidates就会包含它
*
*
* 三个Filter的过滤条件是“逻辑与”的关系
*/
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);//如果Filter实现了XxxAware接口,在这里会将XXXAware添加到对应的Filter当中
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
/**
* 打印下面的内容是想在match里面打条件断点,看到底如何匹配的
*/
if (!skip[i]) {
logger.debug("++++++matched : "+candidates[i]);
result.add(candidates[i]);
}else{
logger.debug("------unmatched : "+configurations.get(i));
}
}
。。。省略部分代码。。。
return new ArrayList<>(result);
}
执行完上面的一顿骚操作,看看结果怎么样
最终找到22个需要自动配置的类
随便拿一个出来看看,那就用org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
它应该很有代表性吧,凡是用过SpringMVC的都知道DispatcherServlet的地位的。
在下图中设置条件断点:
条件断点内容:
entry.getImportClassName().equals("org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration")
重新跑程序,等程序停在这里,再一步步进去看,具体过程文字不太好描述,结合DispatcherServletAutoConfiguration代码说一下大概
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
。。。省略部分代码。。。
/**
* Spring的ConfigurationClassParser能处理嵌套配置类
*/
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
。。。省略部分代码。。。
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
/**
* 会往容器中添加一个DispatcherServlet
*/
DispatcherServlet dispatcherServlet = new DispatcherServlet();
。。。省略部分代码。。。
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
/**
* 往容器中注册一个文件上传的resolver
*/
return resolver;
}
}
/**
* Spring的ConfigurationClassParser能处理嵌套配置类
*/
@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
。。。省略部分代码。。。
/**
* 注册一个DispatcherServlet的FactoryBean
* Tomcat会根据initializer拿到DefaultListableBeanFactory,再从里面去拿ServletContextInitializer的BeanDefinition,然后实例化对象DispatcherServlet
* @param dispatcherServlet
* @return
*/
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
。。。省略部分代码。。。
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
。。。省略部分代码。。。
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
。。。省略部分代码。。。
}
}
为了减少篇幅,将代码中的很多删除了,为了说明ConfigurationClassParser对它的处理,所以只关注类结构,不关注类里面每行代码的作用,伪代码:
@Configurationpublic class DispatcherServletAutoConfiguration{
@Configuration
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration{
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(){}
@Bean
public MultipartResolver multipartResolver(MultipartResolver resolver){}
}
@Configuration
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration{
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet t){}
}
private static class DefaultDispatcherServletCondition extends SpringBootCondition{}
private static class DispatcherServletRegistrationCondition extends SpringBootCondition{}
}
上面代码重点写了三个@Configuration,因为ConfigurationClassParser在解析的时候,不仅会对DispatcherServletAutoConfiguration做处理,还会对嵌套类做解析处理,那么这段处理逻辑代码在Spring源码中何处喃
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 递归处理嵌套类
processMemberClasses(configClass, sourceClass);
}
。。。省略若干代码。。。
}
到此为止自动配置的关键逻辑已经说了90%了,看看DispatcherServletAutoConfiguration配置类完了之后DefaultListableBeanFactory中有了什么变化
很遗憾,它并没有什么变化,当完成parse后并不会注册任何BeanDefinition,而是在configurationClassParser.configurationClasses添加了三个ConfigurationClass:
DispatcherServletAutoConfiguration
DispatcherServletConfiguration
DispatcherServletRegistrationConfiguration
并且分别在3个ConfigurationClass.beanMethods中添加了几个BeanMethod,但是DispatcherServletConfiguration因为有@EnableConfigurationProperties,所以在它的ConfigurationClass中还有importBeanDefinitionRegistrar,那什么时候DefaultListableBeanFactory才会有变化喃。
答案是:ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
/**
* 循环在ConfigurationClassParser.parse中生成的ConfigurationClass,处理里面的beanMethods、importedResources、importBeanDefinitionRegistrars
*/
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
处理前
当三个ConfiguraionClass处理完之后
这期间往DefaultListableBeanFactory中注册了7个BeanDefinition,它们分别是:
DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
DispatcherServletConfiguration
往容器中注册了如下三个BeanDefinition
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
dispatcherServlet
spring.http-org.springframework.boot.autoconfigure.http.HttpProperties
spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
DispatcherServletRegistrationConfiguration
dispatcherServletRegistration -> {ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition@4788}Collecting data…
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
其他的配置类也是同样的处理方式,只不过可能配置上面的注解不太一样,会稍微有一些不同。
导致位置AutoConfigurationImportSelector的工作结束了,自动配置的原理也说完了
AutoConfigurationPackages.Registrar
往DefaultListableBeanFactory中注册一个名为org.springframework.boot.autoconfigure.AutoConfigurationPackages的BasePackage的BeanDefinition,并且设置了构造函数参数为启动类的当前目录,比如:com.jv.springboot
以上是 Springboot源码分析启动流程自动配置详解 的全部内容, 来源链接: utcz.com/z/512009.html