SpringBoot自动配置

SpringBoot的一个很重要的特性就是自动配置,使用注解,让服务不再需要xml等各种配置文件
然后使用main方法一键启动

@SpringBootApplication

@SpringBootApplication 注解是 SpringBoot 的源头,一切都要从该注解开始说起

标注在类上时,表示该类是 SpringBoot 的主配置类,并从该类中的 main 方法来启动 SpringBoot 应用

注解源码如下:

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

可以看出主要包含了三个注解:

  • @SpringBootConfiguration
  • @ComponentScan
  • @EnableAutoConfiguration

@SpringBootConfiguration

1
2
3
4
5
6
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

@SpringBootConfiguration 的源码发现其实就是一个 @Configuration 注解,表示这是一个 SpringBoot 的配置类

@ComponentScan

@ComponentScan 表示开启组件扫描

默认是扫描当前类下的package。将 @Controller/@Service/@Component/@Repository 等注解加载到IOC容器中

@EnableAutoConfiguration

@EnableAutoConfiguration 表示开启自动配置,SpringBoot 自动配置的原理都在这个注解中

1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

@EnableAutoConfiguration 又主要包含 @Import@AutoConfigurationPackage 俩个注解,先看下重点 @Import 这个注解

@Import

@Import 注解是用来导入配置类或者一些需要前置加载的类,可以将多个配置类融合成一个配置类

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();

}

看代码可以看出,有三种使用方式:

  • 导入普通 Java 类,导入的类会被加载为 Bean
  • 导入 ImportSelector 接口实现
  • 导入 ImportBeanDefinitionRegistrar 接口实现
  1. ImportSelector

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface ImportSelector {

    /**
    * Select and return the names of which class(es) should be imported based on
    * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
    */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

    }

    接口中只有一个 selectImports 方法,用于返回全类名数组,会把返回数组中的类全部导入

  2. ImportBeanDefinitionRegistrar

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      public interface ImportBeanDefinitionRegistrar {

    /**
    * Register bean definitions as necessary based on the given annotation metadata of
    * the importing {@code @Configuration} class.
    * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
    * registered here, due to lifecycle constraints related to {@code @Configuration}
    * class processing.
    * @param importingClassMetadata annotation metadata of the importing class
    * @param registry current bean definition registry
    */
    public void registerBeanDefinitions(
    AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

    }

    registerBeanDefinitions 它可以手动注册bean到容器中

AutoConfigurationImportSelector.class

AutoConfigurationImportSelector.class 类实现了 ImportSelector 接口,其中的核心方法是 selectImports,来返回需要导入的组件的全类名数组

  • selectImports

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader);
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //获取全类名数组
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
    attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
    }
  • getCandidateConfigurations

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
    AnnotationAttributes attributes) {
    //获取EnableAutoConfiguration.class对应的全类名数组
    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;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
    }
  • loadFactoryNames

    1
    2
    3
    4
    5
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //返回 META-INF/spring.factories 文件下 EnableAutoConfiguration.class 对应的所有值
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
  • loadSpringFactories

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //从当前项目的类路径中获取所有 META-INF/spring.factories 这个文件下的信息。
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
    return result;
    }

    try {
    //FACTORIES_RESOURCE_LOCATION:META-INF/spring.factories
    Enumeration<URL> urls = (classLoader != null ?
    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    result = new LinkedMultiValueMap<>();
    while (urls.hasMoreElements()) {
    URL url = urls.nextElement();
    UrlResource resource = new UrlResource(url);
    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    for (Map.Entry<?, ?> entry : properties.entrySet()) {
    String factoryClassName = ((String) entry.getKey()).trim();
    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    result.add(factoryClassName, factoryName.trim());
    }
    }
    }
    cache.put(classLoader, result);
    return result;
    }
    catch (IOException ex) {
    throw new IllegalArgumentException("Unable to load factories from location [" +
    FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    }

META-INF/spring.factories

META-INF/spring.factories 在很多第三方依赖中都会有这个文件,第三方依赖通过这个文件并配置 EnableAutoConfiguration 参数,然后自动配置会把这个参数下面的所有类都配置到 Spring 容器中,如图

image

@AutoConfigurationPackage

@EnableAutoConfiguration 注解中的另一个注解 @AutoConfigurationPackage,是用来自动配置包的,将主配置类 @SpringBootConfiguration 标注的类,所在包及下面所有子包里面的所有组件扫描到 Spring 容器中

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

可以看到,AutoConfigurationPackage 注解依然使用了 import 注解,不过使用的是 ImportBeanDefinitionRegistrar 接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

@Override
//把所在包及其子包里边的组件扫描注册到Spring容器中
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}

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

}

总结

SpringBoot的自动装配是由 @EnableAutoConfiguration 注解来实现的,而 @EnableAutoConfiguration 注解又是通过 @AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class) 来实现服务自身的自动配置和第三方依赖的自动配置

注解关系如图:

image

分享到: