详解SpringBoot简化配置分析总结

在SpringBoot启动类中,该主类被@SpringBootApplication所修饰,跟踪该注解类,除元注解外,该注解类被如下自定注解修饰。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

让我们简单叙述下它们各自的功能:

  • @ComponentScan:扫描需要被IoC容器管理下需要管理的Bean,默认当前根目录下的
  • @EnableAutoConfiguration:装载所有第三方的Bean
  • @SpringBootConfiguration 作用等同于@Configuration

我们来看下@SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
 @AliasFor(
  annotation = Configuration.class
 )
 boolean proxyBeanMethods() default true;
}

可以看到该注解类内包含与@Configuration,其作用与@Configuration并无太大区别,只是多了层属性嵌套。

故: @SpringBootConfiguration + @ComponentScan

将根目录下所有被**@Controller、@Service、@Repository、@Component**等所修饰的类交给IoC容器管理。

那么重点来了,@EnableAutoConfiguration是如何装载第三方Bean的呢?让我们跟踪下它的源码。

首先我们可以看到该类被如下注解修饰:

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})

我们先关注下AutoConfigurationImportSelector这个组件。

// 批量导入第三方的一些Bean
@Import({AutoConfigurationImportSelector.class})

其中该组件的selectImports(AnnotationMetadata annotationMetadata)方法,我们先简述下它的作用:扫描所有需要被管理的第三方Bean并交给IoC容器进行管理。然后我们接着往下追踪。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
 if (!this.isEnabled(annotationMetadata)) {
  return NO_IMPORTS;
 } else {
  // 让我们跟踪到这个方法
  AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
 }
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
 if (!this.isEnabled(annotationMetadata)) {
  return EMPTY_ENTRY;
 } else {
  AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
  // 获取所有AutoConfiguration的配置类
  List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
  // 下面就是对AutoConfiguration的去重、排除和过滤等操作
  configurations = this.removeDuplicates(configurations);
  Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
  this.checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);
  configurations = this.getConfigurationClassFilter().filter(configurations);
  // 我们继续追踪这里
  this.fireAutoConfigurationImportEvents(configurations, exclusions);
  return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
 }
}
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
 List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
 if (!listeners.isEmpty()) {
  // 加了层包装
  AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
  Iterator var5 = listeners.iterator();

  while(var5.hasNext()) {
   AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
   this.invokeAwareMethods(listener);
   // 向ConditionEvaluationReport中导入所有AutoConfiguration
   listener.onAutoConfigurationImportEvent(event);
  }
 }

}

可以猜想IoC容器在启动时会将这里的AutoConfiguration中的每个Bean都注入到容器中。这里的源码我们先跟踪到这里,大致了解了下该方法的作用。

那么SpringBoot又是如何取感知第三方的Bean文件呢?

SpringBoot和第三方Bean之间存在一定的规定。即通过对于相应依赖的Jar包中可能存在一个spring.factories文件,在该文件中就记录了需要被IoC容器管理的Bean文件路径,SpringBoot通过该文件确定需要IoC管理的Bean文件位置。对于spring-boot-autoconfiguration的spring.factories文件中,记录着大量xxxAutoConfiguration的类文件位置,这些类都被@Configuration注解标识,即这些配置类会配置多个Bean从而解决spring.factories可能产生的臃肿问题。

详解SpringBoot简化配置分析总结

扫一扫手机访问