SpringBoot自动配置原理


h2coder SpringBoot自动配置原理 - 图1 2020年08月20日 阅读 379# SpringBoot自动配置原理
SpringBoot现在基本是标配了,除非是老旧的项目,或者保守一点的企业,都会选择SpringBoot。
在SpringBoot出现之前,使用SSH或者SSM,都要很多的XML配置文件,而这些配置,在每个项目中,大部分都是相同的。
虽然都一样,但项目都要配置,可能会出现配置几小时,写代码几分钟的情况,把项目启动拖慢了。SpringBoot则是为了解决这种问题而生的,提高开发效率。
用过SpringBoot的小伙伴都知道,在IDEA使用SpringBoot Initializer,快速配置项目,写一个Controller就可以,快速搭建起Web项目。
SpringBoot给我们提供了大量的starter,里面已经帮我们配置了常用配置,如果我们需要改动,则在application.yml中配置即可。
SpringBoot之所以可以这样做,是因为它的设计策略,开箱即用和约定大于配置。
下面我们看下SpringBoot帮我们做了什么吧!

自动装配

要使用SpringBoot,我们需要指定parent父工程

基础配置

pom文件指定parent父工程

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.2.0.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>
  7. 复制代码

点进去会发现,spring-boot-starter-parent也有父工程,就是spring-boot-dependencies,继续点进去

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-dependencies</artifactId>
  4. <version>2.2.0.RELEASE</version>
  5. <relativePath>../../spring-boot-dependencies</relativePath>
  6. </parent>
  7. 复制代码

spring-boot-dependencies看来是管理依赖和版本号的,所以我们依赖第三方库时,如果在这个依赖列表中有,则不需要写版本号了

  1. <properties>
  2. <activemq.version>5.15.10</activemq.version>
  3. <antlr2.version>2.7.7</antlr2.version>
  4. <appengine-sdk.version>1.9.76</appengine-sdk.version>
  5. <artemis.version>2.10.1</artemis.version>
  6. <aspectj.version>1.9.4</aspectj.version>
  7. <assertj.version>3.13.2</assertj.version>
  8. <atomikos.version>4.0.6</atomikos.version>
  9. <awaitility.version>4.0.1</awaitility.version>
  10. <bitronix.version>2.1.4</bitronix.version>
  11. <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
  12. <byte-buddy.version>1.10.1</byte-buddy.version>
  13. ...太多了,省略其他
  14. </properties>
  15. <dependencyManagement>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot</artifactId>
  20. <version>2.2.0.RELEASE</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-test</artifactId>
  25. <version>2.2.0.RELEASE</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-test-autoconfigure</artifactId>
  30. <version>2.2.0.RELEASE</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-actuator</artifactId>
  35. <version>2.2.0.RELEASE</version>
  36. </dependency>
  37. <dependencies>
  38. ...太多了,省略其他
  39. <dependencyManagement>
  40. 复制代码

我们的父工程spring-boot-starter-parent,还帮我们指定了配置文件的格式

  1. <build>
  2. <resources>
  3. <resource>
  4. <filtering>true</filtering>
  5. <directory>${basedir}/src/main/resources</directory>
  6. <!-- 指定了配置文件的格式,加载顺序为yml => yaml => properties -->
  7. <includes>
  8. <include>**/application*.yml</include>
  9. <include>**/application*.yaml</include>
  10. <include>**/application*.properties</include>
  11. </includes>
  12. </resource>
  13. <resource>
  14. <directory>${basedir}/src/main/resources</directory>
  15. <excludes>
  16. <exclude>**/application*.yml</exclude>
  17. <exclude>**/application*.yaml</exclude>
  18. <exclude>**/application*.properties</exclude>
  19. </excludes>
  20. </resource>
  21. </resources>
  22. <build>
  23. 复制代码

启动器 starter

SpringBoot将每种使用场景所需要的依赖和依赖,封装成一个启动器starter,我们需要引入某种领域的功能时,直接依赖对应的starer即可。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter</artifactId>
  4. <version>2.2.0.RELEASE</version>
  5. </dependency>
  6. 复制代码

例如我们常用的Web开发,需要依赖SpringMVC等,SpringBoot提供了spring-boot-starter-web启动器

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. 复制代码

我们点进行该starter,他给我们定义了以下依赖:

  1. spring-boot-starter SpringBoot基础启动器
  2. spring-boot-starter-json json序列化、反序列化的启动器
  3. spring-boot-starter-tomcat 内嵌Tomcat
  4. spring-web和spring-webmvc,就是我们的SpringMVC
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter</artifactId>
    5. <version>2.2.0.RELEASE</version>
    6. <scope>compile</scope>
    7. </dependency>
    8. <dependency>
    9. <groupId>org.springframework.boot</groupId>
    10. <artifactId>spring-boot-starter-json</artifactId>
    11. <version>2.2.0.RELEASE</version>
    12. <scope>compile</scope>
    13. </dependency>
    14. <dependency>
    15. <groupId>org.springframework.boot</groupId>
    16. <artifactId>spring-boot-starter-tomcat</artifactId>
    17. <version>2.2.0.RELEASE</version>
    18. <scope>compile</scope>
    19. </dependency>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-starter-validation</artifactId>
    23. <version>2.2.0.RELEASE</version>
    24. <scope>compile</scope>
    25. <exclusions>
    26. <exclusion>
    27. <artifactId>tomcat-embed-el</artifactId>
    28. <groupId>org.apache.tomcat.embed</groupId>
    29. </exclusion>
    30. </exclusions>
    31. </dependency>
    32. <dependency>
    33. <groupId>org.springframework</groupId>
    34. <artifactId>spring-web</artifactId>
    35. <version>5.2.0.RELEASE</version>
    36. <scope>compile</scope>
    37. </dependency>
    38. <dependency>
    39. <groupId>org.springframework</groupId>
    40. <artifactId>spring-webmvc</artifactId>
    41. <version>5.2.0.RELEASE</version>
    42. <scope>compile</scope>
    43. </dependency>
    44. </dependencies>
    45. 复制代码

    启动类 SpringBootApplication

    SpringBoot要求我们提供一个启动类,并且类头加上 @SpringBootApplication注解,该注解就是SpringBoot启动的核心。
    1. @SpringBootApplication
    2. public class SpringbootEsApplication {
    3. public static void main(String[] args) {
    4. SpringApplication.run(SpringbootEsApplication.class, args);
    5. log.info("项目启动成功,访问地址:http://localhost:8081/");
    6. }
    7. }
    8. 复制代码
    我们点进去@SpringBootApplication注解
    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @SpringBootConfiguration
    6. @EnableAutoConfiguration
    7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    9. @ConfigurationPropertiesScan
    10. public @interface SpringBootApplication {
    11. //省略属性...
    12. }
    13. 复制代码
    我们会发现SpringBootApplication是一个复合注解,当中最重要的是@SpringBootConfiguration@EnableAutoConfiguration,这2个注解。 @ComponentScan注解是包扫描,因为没有配置扫描包,默认是扫描标识该注解的类的包,以及它以下的子包,所以启动类一般在根包下。
  • @SpringBootConfiguration注解

我们发现@SpringBootConfiguration注解 ,最主要是加上了@Configuration注解。 我们知道@Configuration注解 就代表了一个JavaConfig方式的Spring的容器,所以我们启动器类也相当于一个容器。
SpringBootConfiguration注解没什么可看了,我们看下一个注解

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Configuration(
  5. proxyBeanMethods = false
  6. )
  7. public @interface SpringBootConfiguration {
  8. @AliasFor(
  9. annotation = Configuration.class
  10. )
  11. boolean proxyBeanMethods() default true;
  12. }
  13. 复制代码
  • @EnableAutoConfiguration注解

@EnableAutoConfiguration注解中,主要注解是@Import(AutoConfigurationImportSelector.class)。 @Import注解,帮我们导入了AutoConfigurationImportSelector这个类

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {
  8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  9. Class<?>[] exclude() default {};
  10. String[] excludeName() default {};
  11. }
  12. 复制代码
  • AutoConfigurationImportSelector类

AutoConfigurationImportSelector类实现了DeferredImportSelector接口,该接口继承ImportSelector接口 ,会要求复写selectImports()方法。
ImportSelector接口,主要是为了导入@Configuration配置的,而DeferredImportSelector是延期导入,当所有的@Configuration都处理完成后,再调用DeferredImportSelector进行处理。
所以AutoConfigurationImportSelector类是延迟导入的,所有@Configuration都处理完后,再调用它的selectImports()方法。
selectImports()方法,调用了getAutoConfigurationEntry()方法,而getAutoConfigurationEntry()又调用了getCandidateConfigurations()方法。 而getCandidateConfigurations()方法是重点!

  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  2. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  3. @Override
  4. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  5. if (!isEnabled(annotationMetadata)) {
  6. return NO_IMPORTS;
  7. }
  8. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  9. .loadMetadata(this.beanClassLoader);
  10. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
  11. annotationMetadata);
  12. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  13. }
  14. protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
  15. AnnotationMetadata annotationMetadata) {
  16. if (!isEnabled(annotationMetadata)) {
  17. return EMPTY_ENTRY;
  18. }
  19. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  20. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  21. configurations = removeDuplicates(configurations);
  22. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  23. checkExcludedClasses(configurations, exclusions);
  24. configurations.removeAll(exclusions);
  25. configurations = filter(configurations, autoConfigurationMetadata);
  26. fireAutoConfigurationImportEvents(configurations, exclusions);
  27. return new AutoConfigurationEntry(configurations, exclusions);
  28. }
  29. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  30. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  31. getBeanClassLoader());
  32. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  33. + "are using a custom packaging, make sure that file is correct.");
  34. return configurations;
  35. }
  36. }
  37. 复制代码
  • getCandidateConfigurations()方法

方法中,调用SpringFactoriesLoader.loadFactoryNames(),传入2个参数,EnableAutoConfiguration的Class和Bean的ClassLoader。
loadFactoryNames()方法,返回一个集合,如果集合为空,进入下一句的Assert断言,就会抛出异常。
最后返回这个配置集合。

  1. //AutoConfigurationImportSelector
  2. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  3. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  4. getBeanClassLoader());
  5. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  6. + "are using a custom packaging, make sure that file is correct.");
  7. return configurations;
  8. }
  9. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  10. return EnableAutoConfiguration.class;
  11. }
  12. protected ClassLoader getBeanClassLoader() {
  13. return this.beanClassLoader;
  14. }
  15. 复制代码
  • SpringFactoriesLoader

loadFactoryNames()方法,获取了传入的EnableAutoConfiguration注解的Class,调用loadSpringFactories()方法。 而loadSpringFactories()方法,会读取jar包中META-INF目录的spring.factories配置文件。
如果读取不到,则返回一个空集合。

  1. public final class SpringFactoriesLoader {
  2. //jar包中的META-INF目录下,spring.factories配置文件
  3. public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
  4. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  5. String factoryTypeName = factoryType.getName();
  6. return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
  7. }
  8. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  9. MultiValueMap<String, String> result = cache.get(classLoader);
  10. if (result != null) {
  11. return result;
  12. }
  13. try {
  14. Enumeration<URL> urls = (classLoader != null ?
  15. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  16. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  17. result = new LinkedMultiValueMap<>();
  18. while (urls.hasMoreElements()) {
  19. URL url = urls.nextElement();
  20. UrlResource resource = new UrlResource(url);
  21. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  22. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  23. String factoryTypeName = ((String) entry.getKey()).trim();
  24. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  25. result.add(factoryTypeName, factoryImplementationName.trim());
  26. }
  27. }
  28. }
  29. cache.put(classLoader, result);
  30. return result;
  31. }
  32. catch (IOException ex) {
  33. throw new IllegalArgumentException("Unable to load factories from location [" +
  34. FACTORIES_RESOURCE_LOCATION + "]", ex);
  35. }
  36. }
  37. }
  38. 复制代码
  • spring.factories配置文件

我们选一个starter,例如spring-boot-autoconfigure,找到它的META-INF目录,找到spring.factories文件,打开。
我们发现文件里,配置了很多自动配置属性(内容有删减,实在太多了!)。 它的形式是Key-Value,例如其中一个Key是EnableAutoConfiguration的全类名,它的Value是好几个名字以AutoConfiguration结尾的类,每个类之间用逗号分隔。
刚才我们跟踪的loadFactoryNames()方法,传入的EnableAutoConfiguration的Class,就是要从spring.factories配置文件中找到它对应的那一组Value。
我们以ServletWebServerFactoryAutoConfiguration为例,点进去看一下

  1. # 省略其他配置...
  2. # Auto Configure !!!!!!!! 重点在这里 !!!!!!!!
  3. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  4. org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
  5. org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
  6. org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
  7. org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
  8. org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
  9. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
  10. org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
  11. org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
  12. org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
  13. org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
  14. org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
  15. # 省略其他配置...
  16. 复制代码
  • ServletWebServerFactoryAutoConfiguration类

我们看到该类的类头上,有@EnableConfigurationProperties注解,该属性表示加载配置属性,这里指定了一个ServerProperties类。
我们点进去ServerProperties类看一下

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. @ConditionalOnClass(ServletRequest.class)
  4. @ConditionalOnWebApplication(type = Type.SERVLET)
  5. @EnableConfigurationProperties(ServerProperties.class)
  6. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  7. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  8. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  9. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  10. public class ServletWebServerFactoryAutoConfiguration {
  11. //...省略
  12. }
  13. 复制代码
  • ServerProperties类

这个是一个和配置信息相对应的类,它类头上配置了@ConfigurationProperties注解,它可以将配置文件中的配置项的内容,映射到我们的类的变量上。
注解上,配置的prefix属性,就代表了server.xxx系列配置,例如我们配置端口:server.port,该注解将我们的配置映射到ServerProperties上。

  1. @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
  2. public class ServerProperties {
  3. /**
  4. * Server HTTP port.
  5. */
  6. private Integer port;
  7. /**
  8. * Network address to which the server should bind.
  9. */
  10. private InetAddress address;
  11. }
  12. 复制代码

到此为止,自动配置的流程基本通了,总结一下:
SpringBoot启动类的main方法启动时,会找@EnableAutoConfiguration注解,而该注解就在@SpringBootApplication上。而@EnableAutoConfiguration注解上,使用了@Import注解,导入了AutoConfigurationImportSelector类。 而该类,会去找META-INF/spring.factories配置文件,这个配置文件中配置了一系列的以AutoConfiguration结尾的类,就是自动配置类。 而每个配置类,都有一个Properties结尾的配置类,它和我们在yml中的配置项时一一对应的,相当于绑定配置到了该对象中。
如果只是想面试了解一下,到这里就可以了,而如果更想深入,就要继续跟一下。
如果要继续跟,就还有一个疑点,自动装配是什么时候开始的呢,其实就是AutoConfigurationImportSelector类上的selectImports()方法,还不知道它什么会被调用。

何时开始进行自动装配

我们回归到Spring,Spring应用启动,会在AbstractApplicationContext类中,调用refresh()方法。
refresh()方法中,调用了invokeBeanFactoryPostProcessors()方法,该方法是用来处理BeanFactoryPostProcessor接口的,而BeanFactoryPostProcessor的有一个子接口BeanDefinitionRegistryPostProcessor

  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  2. implements ConfigurableApplicationContext {
  3. @Override
  4. public void refresh() throws BeansException, IllegalStateException {
  5. //省略无关代码...
  6. invokeBeanFactoryPostProcessors(beanFactory);
  7. //省略无关代码...
  8. }
  9. }
  10. //BeanDefinitionRegistryPostProcessor
  11. public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
  12. void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
  13. }
  14. 复制代码
  • ConfigurationClassPostProcessor类

子接口BeanDefinitionRegistryPostProcessor,有一个实现类ConfigurationClassPostProcessor,它是专门处理@Configuration注解的。
processConfigBeanDefinitions()方法中,就是处理@Configuration注解的类。主要是使用ConfigurationClassParser类的parse()方法。
我们进去parse()方法,看一下

  1. public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
  2. PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
  3. public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
  4. //省略部分代码
  5. // Parse each @Configuration class
  6. ConfigurationClassParser parser = new ConfigurationClassParser(
  7. this.metadataReaderFactory, this.problemReporter, this.environment,
  8. this.resourceLoader, this.componentScanBeanNameGenerator, registry);
  9. Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
  10. Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
  11. do {
  12. //解析处理@Configuration注解的类
  13. parser.parse(candidates);
  14. parser.validate();
  15. Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
  16. configClasses.removeAll(alreadyParsed);
  17. // Read the model and create bean definitions based on its content
  18. if (this.reader == null) {
  19. this.reader = new ConfigurationClassBeanDefinitionReader(
  20. registry, this.sourceExtractor, this.resourceLoader, this.environment,
  21. this.importBeanNameGenerator, parser.getImportRegistry());
  22. }
  23. this.reader.loadBeanDefinitions(configClasses);
  24. alreadyParsed.addAll(configClasses);
  25. candidates.clear();
  26. if (registry.getBeanDefinitionCount() > candidateNames.length) {
  27. String[] newCandidateNames = registry.getBeanDefinitionNames();
  28. Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
  29. Set<String> alreadyParsedClasses = new HashSet<>();
  30. for (ConfigurationClass configurationClass : alreadyParsed) {
  31. alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
  32. }
  33. for (String candidateName : newCandidateNames) {
  34. if (!oldCandidateNames.contains(candidateName)) {
  35. BeanDefinition bd = registry.getBeanDefinition(candidateName);
  36. if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
  37. !alreadyParsedClasses.contains(bd.getBeanClassName())) {
  38. candidates.add(new BeanDefinitionHolder(bd, candidateName));
  39. }
  40. }
  41. }
  42. candidateNames = newCandidateNames;
  43. }
  44. }
  45. while (!candidates.isEmpty());
  46. }
  47. }
  48. 复制代码
  • ConfigurationClassParser类的parse()方法

首先类中有一个内部类DeferredImportSelectorHandler,构造方法ConfigurationClassParser实例时,就创建该内部类的实例。
parse()方法调用时,最后一句调用了processDeferredImportSelectors()方法。

  1. class ConfigurationClassParser {
  2. public void parse(Set<BeanDefinitionHolder> configCandidates) {
  3. this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
  4. for (BeanDefinitionHolder holder : configCandidates) {
  5. BeanDefinition bd = holder.getBeanDefinition();
  6. try {
  7. if (bd instanceof AnnotatedBeanDefinition) {
  8. parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  9. }
  10. else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
  11. parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  12. }
  13. else {
  14. parse(bd.getBeanClassName(), holder.getBeanName());
  15. }
  16. }
  17. catch (BeanDefinitionStoreException ex) {
  18. throw ex;
  19. }
  20. catch (Throwable ex) {
  21. throw new BeanDefinitionStoreException(
  22. "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  23. }
  24. }
  25. //重点
  26. processDeferredImportSelectors();
  27. }
  28. }
  29. 复制代码
  • processDeferredImportSelectors()方法

重点在String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
调用的是DeferredImportSelectorHolder类,它保存了DeferredImportSelector的引用,在这个for循环中,调用了DeferredImportSelectorselectImports()方法,从而调用到了我们之前分析的AutoConfigurationImportSelector类中的selectImports()方法了。

  1. private void processDeferredImportSelectors() {
  2. List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
  3. this.deferredImportSelectors = null;
  4. Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
  5. for (DeferredImportSelectorHolder deferredImport : deferredImports) {
  6. ConfigurationClass configClass = deferredImport.getConfigurationClass();
  7. try {
  8. String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
  9. processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
  10. }
  11. catch (BeanDefinitionStoreException ex) {
  12. throw ex;
  13. }
  14. catch (Throwable ex) {
  15. throw new BeanDefinitionStoreException(
  16. "Failed to process import candidates for configuration class [" +
  17. configClass.getMetadata().getClassName() + "]", ex);
  18. }
  19. }
  20. }
  21. //该类,保存了配置类和DeferredImportSelector的引用
  22. private static class DeferredImportSelectorHolder {
  23. private final ConfigurationClass configurationClass;
  24. private final DeferredImportSelector importSelector;
  25. public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
  26. this.configurationClass = configClass;
  27. this.importSelector = selector;
  28. }
  29. public ConfigurationClass getConfigurationClass() {
  30. return this.configurationClass;
  31. }
  32. public DeferredImportSelector getImportSelector() {
  33. return this.importSelector;
  34. }
  35. }
  36. 复制代码

参考资料

SpringBoot:认认真真梳理一遍自动装配原理
Spring Boot面试杀手锏 — 自动配置原理
深入理解SpringBoot之自动装配