1, SpringBootApplication注解详解

SpringBoot 中, 我们不用配置bean.xml, 也不用配置 @ComponentScan注解 , 那为什么启动类同级包及子包中的组件类依然能被IOC创建并管理, 但除此之外的包的组件类就扫描不到呢?

@SpringBootApplication

点开此注解后, 我们看到

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication {}

原来

@SpringBootApplication == @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan(…)


@SpringBootConfiguration

@SpringBootConfiguration 是继承于 @Configuration 的, 二者功能一致

对于 @Configuration 的讲解请看下篇文章
@Configuration注解分析


@ComponentScan

作用相当于 Spring 中的 context:component-scan 标签, 用来扫描带有组件标识的类, 以便于注册到 IOC 中.
按照官方的说法, @ComponentScan 注解可不加任何参数, 不加参数默认扫描当前类同级包及子包组件类.


常用参数
1, basePackageClasses : 用来扫描带有组件标识的类(例子中的String, Integer无组件标识, 所以会抛出异常)
例: @ComponentScan(basePackageClasses = {Test1.class, Test2.class})
      也可以扫描一个类 -> @ComponentScan(basePackageClasses = Test.class)
2, basePackages : 用来扫描包中带有组件标识的类
例: @ComponentScan(basePackages = {“com.baidu.pojo”, “com.ali.pojo”})
      也可以扫描一个包 -> @ComponentScan(basePackages = “com.baidu.pojo”)


@EnableAutoConfiguration

@EnableAutoConfiguration 注解是基于 @Import 将所有符合注册条件的 Bean 加载到 IOC 容器中

打开 @EnableAutoConfiguration 注解

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

@EnableAutoConfiguration == @AutoConfigurationPackage + @Import

@AutoConfigurationPackage

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

@Import(AutoConfigurationPackages.Registrar.class) 给容器中导入 AutoConfigurationPackages 的静态内部类: Registrar

Registrar源码:

/**
 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
 * configuration.
 */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    /**
     * 根据传入的元注解信息获取所在的包, 将包中组件类封装为数组进行注册, 
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }

}

@AutoConfigurationPackage 默认将主配置类( @SpringBootApplication )所在的包及其子包里面的所有组件扫描到IOC容器中。

@Import

@Import(AutoConfigurationImportSelector.class)

@Import(AutoConfigurationImportSelector.class) 给容器中导入 AutoConfigurationImportSelector 组件

AutoConfigurationImportSelector继承关系:

1, SpringBootApplication注解详解_第1张图片

实现了一些 SpringxxxAware, 引入了一些必要组件之外, 还实现了 ImportSelector 接口

AutoConfigurationImportSelector 接口何时被执行?

SpringBoot 启动时会使用 ConfigurationClassParser 来解析被 @Configuration 标识的配置类, 然后再处理这个类内部被其他注解修饰的情况, 比如 @Import 注解, @ComponentScan 注解,@Bean 注解等

如果发现注解中存在 @Import(ImportSelector) 的情况下,就会创建一个相应的 ImportSelector 对象,并调用其 process 方法

process方法:

@Override
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()));
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        .getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

process 方法中, 实现真正逻辑的是在 第8行 调用的 getAutoConfigurationEntry 方法

getAutoConfigurationEntry:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    
    // 获取 xxxConfiguration 类
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

打断点之后发现 ->
1, SpringBootApplication注解详解_第2张图片

利用 getAutoConfigurationEntry 向容器中导入组件

getAutoConfigurationEntry 方法中我们可以看到有个方法: getCandidateConfigurations(annotationMetadata, attributes)

getCandidateConfigurations(annotationMetadata, attributes) 是怎么获取到 127Configuration 类的呢?

getCandidateConfigurations(annotationMetadata, attributes):

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                         getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

在此方法, 我们看到 List(String) configurations = SpringFactoriesLoader.loadFactoryNames(…);

可以推测, 是 SpringFactoriesLoader 帮我们加载了 Configuration

再来看看 SpringFactoriesLoader 中最关键的 loadSpringFactories 方法

loadSpringFactories:
1, SpringBootApplication注解详解_第3张图片

进入 loadFactoryNames 方法,会发现 loadFactoryNames 读取了 ClassPath 下面的 META-INF/spring.factories 文件
1, SpringBootApplication注解详解_第4张图片
这也就说 SpringBoot 一启动, 就要给容器加载的 127 个配置类, 对应上面断点看到的 127

问题来了!

1, SpringBootApplication注解详解_第5张图片在这里插入图片描述

可以看出, SpringBoot 启动一共获取到 129 个 Bean, 除去我们自己的 Bean, 还有必要组件外, 那也不够 127 个, 那剩余的 Configuration 被注册到哪里去了呢?

我们打开任意一个配置类, 以 AopConfiguration 为例:

1, SpringBootApplication注解详解_第6张图片

原来, 并不是每一个 Configuration 类都会被注册的, 加了 @Conditional 以条件注册, 不满足, 则不注册


你可能感兴趣的:(SpringBoot,java,springboot,spring,后端)