spring boot自动配置原理

自动配置原理

spring boot中的@EnableAutoConfiguration注解的作用就是开启自动配置,正是因为开启了自动配置,开发者在使用spring boot的时候就不用再填写繁琐的的配置项了,那么spring boot是如何实现这个自动配置的呢?

  1. spring boot会根据开发者添加的依赖判断是否使用了某个技术,比如在依赖中有DispatcherServlet,那就说明使用了spring mvc技术。
  2. spring boot判断出开发者所使用的技术之后,会从自动配置(AutoConfigure)相关的包下找到该技术相关的配置类。
  3. spring boot会加载这些配置类,如果配置文件有写相关的配置信息的话会将该信息读取到配置类的对象中,然后加载到spring容器中,这样就完成了自动配置了。

如果自动配置AutoConfigure包下没有这些配置类,我们需要手动配置。

在spring boot中自动配置类和配置类命名都是符合下面格式的

自动配置类:

*AutoConfiguration

配置类:

*Properties

在spring boot中使用了很多@Conditional相关的注解,该注解有很多派生的注解用来判断一些情况,通过这些注解,spring boot来判断加载哪些自动配置。

我们可以在application配置文件中添加

debug=true

这样就开启了spring boot的debug模式,在启动spring boot的时候可以在控制台中看到目前已经加载的配置类。

源码分析

在spring boot的主类的上面添加了@SpringBootApplication注解,该注解中包含了@EnableAutoConfiguration注解,该注解的作用就是开启自动配置,那么它是如何实现的呢?

在@EnableAutoConfiguration注解中引入了AutoConfigurationImportSelector类。在该类中有一个selectImports方法,代码如下:

 public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        //通过该方法获取的数据,最终被转成数组返回
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

上面中将autoConfigurationEntry.getConfigurations()转成数组返回了,该数据是通过getAutoConfigurationEntry方法返回的,我们进入到该方法内看下:

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        //这里获取了一个list类型的配置数据
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        configurations = this.removeDuplicates(configurations);
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.filter(configurations, autoConfigurationMetadata);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

在该方法中通过调用getCandidateConfigurations方法获取了一个list类型的配置数据,我们进入到该方法中:

 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //这里返回的是list类型的配置数据
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

上面是通过调用了loadFactoryNames方法来获取的list类型的配置数据,我们再进入到该方法内部:

 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里返回的是list类型的配置数据
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

根据上面注释的地方,我们再进入到loadSpringFactories方法里面,在该方法中,可以看到会读取该文件中的内容:

META-INF/spring.factories

也就是说spring boot会读取该文件中的内容,在spring-boot-autoconfigure.jar包中的spring.factories文件里面,可以看到EnableAutoConfiguration配置了很多类。从配置文件中可以找到MultipartAutoConfiguration这个类,该类是文件上传的自动配置类,进入到这个类中,可以看到在类名上面加了下面注解:

@Configuration
加入到spring容器中

@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class,
        MultipartConfigElement.class })
当前项目中要存在Servlet,StandardServletMultipartResolver,MultipartConfigElement这三个类。

@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)

当前application配置文件中spring.servlet.multipart.enabled的如果没有配置,那么他的值是true

@ConditionalOnWebApplication(type = Type.SERVLET)
当前项目需要是一个web项目

@EnableConfigurationProperties(MultipartProperties.class)
将配置文件类MultipartProperties加入到spring容器中

上面会将MultipartProperties这个类加入到spring容器中,我们打开这个类之后,可以看到里面的一些成员变量,这些成员变量就是可以在配置文件中配置的,如果我们没有配置,那么就会使用默认值:

@ConfigurationProperties(prefix = "spring.servlet.multipart", ignoreUnknownFields = false)
public class MultipartProperties {

    /**
     * Whether to enable support of multipart uploads.
     */
    private boolean enabled = true;

    /**
     * Intermediate location of uploaded files.
     */
    private String location;

    /**
     * Max file size.
     */
    private DataSize maxFileSize = DataSize.ofMegabytes(1);

    /**
     * Max request size.
     */
    private DataSize maxRequestSize = DataSize.ofMegabytes(10);

    /**
     * Threshold after which files are written to disk.
     */
    private DataSize fileSizeThreshold = DataSize.ofBytes(0);

    }