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对它的处理,所以只关注类结构,不关注类里面每行代码的作用,伪代码:

@Configuration

public 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

回到顶部