简明易理解的@SpringBootApplication注解源码分析

简介: springboot项目的启动类上,都会有个注解@SpringBootApplication,这个注解起到了什么作用?

springApplication


一、@SpringBootApplication 的作用是什么?


Q:springboot项目的启动类上,都会有个注解@SpringBootApplication,这个注解起到了什么作用?


@SpringBootApplication
public class MicroServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(MicroServiceApplication.class, args);
    }
}
复制代码


我们进入@SpringBootApplication注解,发现它等价于三个注解@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan


@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  ...
}
复制代码


1) @SpringBootConfiguration 就相当于 @Configuration


@Configuration
public @interface SpringBootConfiguration {
}
复制代码


2) @EnabelAutoConfiguration 相当于将这两个类的实例加入到容器中AutoConfigurationImportSelector.class   AutoConfigurationPackages.Registrar.class


@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}
复制代码


@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
复制代码


  • AutoConfigurationImportSelector.class的作用是,注入spring.factories文件中EnableAutoConfiguration对应的类的实例,当然要经过spring.factories文件中AutoConfigurationImportFilter对应的过滤器(OnBeanConditionOnClassConditionOnWebApplicationCondition等等)的过滤。还要排除掉@EnableAutoConfiguration中的exclude和excludeName


具体见ConfigurationClassParser的getImports方法,其中调用了AutoConfigurationImportSelector的process方法和selectImports方法。


public Iterable<Group.Entry> getImports() {
      for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
      }
      return this.group.selectImports();
    }
复制代码


  • AutoConfigurationImportSelector实现了DeferredImportSelector,所以它是解析@Configuration的最后一步。DeferredImportSelector可以和@Order搭配使用。AutoConfigurationImportSelector的意义在于引入其他包时,可以直接注入其他包的@Configuration,当然需要在其他包的resources文件夹下,新建META-INF目录,在META-INF目录下新建spring.factories文件,加入org.springframework.boot.autoconfigure.EnableAutoConfiguration = @Configuration标注的类的路径


  • AutoConfigurationPackages.Registrar.class的作用是,注入一个名称为AutoConfigurationPackages的BasePackages.class实例。这个实例的作用在于保存自动扫描的包路径,供以后使用(比如JPA 的entity扫描)


public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    if (registry.containsBeanDefinition(BEAN)) {
      BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
      ConstructorArgumentValues constructorArguments = beanDefinition
          .getConstructorArgumentValues();
      constructorArguments.addIndexedArgumentValue(0,
          addBasePackages(constructorArguments, packageNames));
    }
    else {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClass(BasePackages.class);
      beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
          packageNames);
      beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
      registry.registerBeanDefinition(BEAN, beanDefinition);
    }
  }
  private static String[] addBasePackages(
      ConstructorArgumentValues constructorArguments, String[] packageNames) {
    String[] existing = (String[]) constructorArguments
        .getIndexedArgumentValue(0, String[].class).getValue();
    Set<String> merged = new LinkedHashSet<>();
    merged.addAll(Arrays.asList(existing));
    merged.addAll(Arrays.asList(packageNames));
    return StringUtils.toStringArray(merged);
  }
复制代码


3) @ComponentScan 当然是将路径下合适的类加载到容器中


Q:它为什么要用到TypeExcludeFilter.classAutoConfigurationExcludeFilter.class 这两个过滤器


@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  ...
}
复制代码


  • 这两个过滤器在ComponentScanAnnotationParser的parse方法中,被加入


public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    //此处加入了三个默认的includeFilter,一个用来筛选@Componment标记的类,一个用来筛选javax.annotation.ManagedBean标记的类,一个用来筛选JSR-330中javax.inject.Named标记的类(如果引入了JSR-330依赖注入标准的话,即引入javax.inject包)
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
        componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    ...
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
        scanner.addExcludeFilter(typeFilter);
      }
    }
    ...
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
        return declaringClass.equals(className);
      }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
  }
复制代码


**也是在parse这个方法里调用了 typeFiltersFor方法,对`TypeFilter.class`的实现类进行了实例化**。(也就是说`TypeExcludeFilter.class`  `AutoConfigurationExcludeFilter.class` `AbstractTypeHierarchyTraversingFilter.class`都在这里进行了实例化,但是没有加入spring的bean池)
复制代码


private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
    List<TypeFilter> typeFilters = new ArrayList<>();
    FilterType filterType = filterAttributes.getEnum("type");
    for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
      switch (filterType) {
        case ANNOTATION:
          Assert.isAssignable(Annotation.class, filterClass,
              "@ComponentScan ANNOTATION type filter requires an annotation type");
          @SuppressWarnings("unchecked")
          Class<Annotation> annotationType = (Class<Annotation>) filterClass;
          typeFilters.add(new AnnotationTypeFilter(annotationType));
          break;
        case ASSIGNABLE_TYPE:
          typeFilters.add(new AssignableTypeFilter(filterClass));
          break;
        case CUSTOM:
          Assert.isAssignable(TypeFilter.class, filterClass,
              "@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
          TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
          ParserStrategyUtils.invokeAwareMethods(
              filter, this.environment, this.resourceLoader, this.registry);
          typeFilters.add(filter);
          break;
        default:
          throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
      }
    }
    for (String expression : filterAttributes.getStringArray("pattern")) {
      switch (filterType) {
        case ASPECTJ:
          typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
          break;
        case REGEX:
          typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
          break;
        default:
          throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
      }
    }
    return typeFilters;
  }
复制代码


  • ComponentScanAnnotationParser的parse方法中,scanner最后为什么又加了一个AbstractTypeHierarchyTraversingFilter呢?我们看一下它的match方法,发现是过滤掉启动类,不让它作为@configuration标记的候选类,避免再一次解析启动类上的各种注解(因为它的两个参数considerInherited和considerInterfaces在scanner.addExcludeFilter这条语句中,设置成了false,导致下面的判断语句不生效)。


public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
      throws IOException {
    ...
    ClassMetadata metadata = metadataReader.getClassMetadata();
    if (matchClassName(metadata.getClassName())) {
      return true;
    }
    if (this.considerInherited) {
      ...
    }
    if (this.considerInterfaces) {
      ...
    }
    return false;
  }
复制代码


  • 注:启动类本身还是会注入到spring的bean池中的,具体见SpringApplication的load方法
  • ExcludeFilter在ClassPathScanningCandidateComponentProvider的isCandidateComponent方法中被使用


protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
        return false;
      }
    }
    for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
        return isConditionMatch(metadataReader);
      }
    }
    return false;
  }
复制代码


  • AutoConfigurationExcludeFilter 的作用是过滤掉会自动配置的配置类,避免重复


@Override
  public boolean match(MetadataReader metadataReader,
      MetadataReaderFactory metadataReaderFactory) throws IOException {
    //如果这个类被@Configuration标注,且属于自动加载的配置,那么过滤它,避免重复
        return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
  }
  private boolean isConfiguration(MetadataReader metadataReader) {
    return metadataReader.getAnnotationMetadata()
        .isAnnotated(Configuration.class.getName());
  }
  private boolean isAutoConfiguration(MetadataReader metadataReader) {
    return getAutoConfigurations()
        .contains(metadataReader.getClassMetadata().getClassName());
  }
  protected List<String> getAutoConfigurations() {
    if (this.autoConfigurations == null) {
            /**
            从META-INF/spring.factories文件中,找出EnableAutoConfiguration.class
            多个jar包中,都存在spring.factories文件
            其中包含EnableAutoConfiguration.class的spring.factories文件,位于spring-boot-autoconfigure
            **/
      this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
          EnableAutoConfiguration.class, this.beanClassLoader);
    }
    return this.autoConfigurations;
  }
复制代码


  • TypeExcludeFilter 的作用是加载spring bean池中所有针对TypeExcludeFilter的扩展,并循环遍历这些扩展类调用其match方法


public boolean match(MetadataReader metadataReader,
      MetadataReaderFactory metadataReaderFactory) throws IOException {
    if (this.beanFactory instanceof ListableBeanFactory
        && getClass() == TypeExcludeFilter.class) {
            //加载spring bean池中所有针对TypeExcludeFilter的拓展
      Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
          .getBeansOfType(TypeExcludeFilter.class).values();
            // 循环遍历,调用其match方法
      for (TypeExcludeFilter delegate : delegates) {
        if (delegate.match(metadataReader, metadataReaderFactory)) {
          return true;
        }
      }
    }
    return false;
  }
复制代码


各位看官还可以吗?喜欢的话,动动手指点个💗,点个关注呗!!谢谢支持!


目录
相关文章
|
4月前
|
缓存 前端开发 Java
Spring问题之@SpringBootApplication注解是什么有什么作用
Spring问题之@SpringBootApplication注解是什么有什么作用
|
3月前
|
Java Spring
Spring的Bean生命周期中@PostConstruct注解
【8月更文挑战第3天】在Spring框架中,`@PostConstruct`注解标示Bean初始化完成后立即执行的方法。它在依赖注入完成后调用,适用于资源加载、属性设置等初始化操作。若方法中抛出异常,可能影响Bean初始化。与之对应,`@PreDestroy`注解的方法则在Bean销毁前执行,用于资源释放。
131 0
|
6月前
|
Java 数据库连接 Spring
揭秘Spring框架中的神秘@Lazy注解
【4月更文挑战第18天】
264 0
|
Java Spring 容器
Spring中的@Lazy注解简析
Spring中的@Lazy注解简析
215 0
|
Java Spring
Spring 如何通过反射获取controller 包下所有的类,以及类上的注解
Spring 如何通过反射获取controller 包下所有的类,以及类上的注解
306 0
|
Java 数据库 Spring
Spring中的@Transactional注解放在类上和方法上有什么不同?
Spring中的@Transactional注解放在类上和方法上有什么不同?
434 0
|
XML Java 数据格式
【SpringBoot2 从0开始】底层注解 - @Conditional、@ImportResource
【SpringBoot2 从0开始】底层注解 - @Conditional、@ImportResource
【SpringBoot2 从0开始】底层注解 - @Conditional、@ImportResource
|
druid Dubbo Java
Spring系列(七):@FactoryBean注解用法介绍
FactoryBean是实现了FactoryBean<T>接口的Bean,可以该Bean的ID从BeanFactory中获取的实际上是FactoryBean中getObject()方法返回的实例对象,而并不是直接FactoryBean本身,想要获取FactoryBean对象本身,可以在id前面加一个&符号来获取
Spring系列(七):@FactoryBean注解用法介绍
|
Java Maven Spring
Spring系列(二):Bean注解用法介绍
今天给大家介绍一下Spring中Bean注解的用法,后续的文章给大家介绍Sping其他注解用法,希望对大家日常工作能有所帮助!
Spring系列(二):Bean注解用法介绍
|
XML SpringCloudAlibaba 前端开发
spring InitializingBean 接口都不知道,源码还是缓缓吧
spring InitializingBean 接口都不知道,源码还是缓缓吧
197 0
spring InitializingBean 接口都不知道,源码还是缓缓吧