SpringBoot自動配置原理學習


介紹

構建Springboot項目時我們會創建一個啟動類

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

1. 在之前使用Spring框架時,我們一般會創建web.xml和spring-context.xml等文件配置組件掃描、調度器、視圖解析器等。

2. 而在SpringBoot中則簡單了很多,這里就有自動配置發揮作用。如默認用的內嵌式容器是 Tomcat ,端口默認設置為 8080

正式進入流程解析

首先看@SpringBootApplication這個注解

@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) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};
    String[] excludeName() default {};

}

@EnableAutoConfiguration的作用是開啟自動配置

核心是@EnableAutoConfiguration注解

這就是Spring Boot自動配置實現的核心入口,重頭戲是@Import注解,這個注解導入了AutoConfigurationImportSelector類,利用AutoConfigurationImportSelector(自動配置導入選擇器)將所有符合自動裝配條件的bean注入到IOC容器中

查看AutoConfigurationImportSelector類代碼,重頭戲是selectImports方法

selectImports何時被執行

Springboot應用啟動過程中使用ConfigurationClassParser分析配置類時,發現注解中存在@Import(ImportSelector)的情況。

就會創建一個相應的ImportSelector對象, 並調用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata)

selectImports()方法流程

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    // 省略。。

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 1 判斷是否開啟自動配置,為false直接返回空字符數組
    
if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }
     // 2 loadMetadata()去加載Spring預先定義的自動配置的依賴信息,
下面會具體說明
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);
     // 3 該方法返回的就是配置項信息,下面會具體說明 AutoConfigurationEntry autoConfigurationEntry
= getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // 省略。。。 }

2. loadMetadata()去加載Spring預先定義的自動配置的依賴信息

 1 static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
 2         try {
 3             Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
 4                     : ClassLoader.getSystemResources(path);
 5             Properties properties = new Properties();
 6             while (urls.hasMoreElements()) {
 7                 properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
 8             }
 9             return loadMetadata(properties);
10         }
11         catch (IOException ex) {
12             throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
13         }
14     }

path的地址是

protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

里面的自動配置文件(部分)

org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.batch.core.launch.JobLauncher
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

  2.1 根據path獲取文件的URL對象

  2.2 遍歷URL節點,將spring-autoconfigure-metadata.properties中的依賴信息加載到properties對象中

  2.3 調用loadMetadata()方法把properties存儲到PropertiesAutoConfigurationMetadata類中

3. 在這里面會調用一個方法getAutoConfigurationEntry(),該方法返回的就是配置項信息,進入這個方法

 1 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
 2             AnnotationMetadata annotationMetadata) {
           // 3.1
3
if (!isEnabled(annotationMetadata)) { 4 return EMPTY_ENTRY; 5 } // 3.2
6
AnnotationAttributes attributes = getAttributes(annotationMetadata);       // 3.3
7
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3.4
8
configurations = removeDuplicates(configurations); // 3.5
9
Set<String> exclusions = getExclusions(annotationMetadata, attributes); 10 checkExcludedClasses(configurations, exclusions); 11 configurations.removeAll(exclusions); 12 configurations = filter(configurations, autoConfigurationMetadata);        // 3.6
13 fireAutoConfigurationImportEvents(configurations, exclusions);        // 3.7
14 return new AutoConfigurationEntry(configurations, exclusions); 15 }

  3.1 判斷是否開啟自動配置

  3.2 getAttributes()方法獲取@SpringBootApplication(主要是為了獲取@EnableAutoConfiguation)注解上的屬性

  3.3 加載META-INF/spring.factories文件, 這個文件配置了具有哪些自動配置類,文件內容如下 ,具體代碼如下    

getCandidateConfigurations方法如下

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;
}

    3.3.1 getSpringFactoriesLoaderFactoryClass方法返回EnableAutoConfiguration.class

    3.3.2 SpringFactoriesLoader.loadFactoryNames會加載META-INF/spring.factories中EnableAutoConfiguration的值中定義的jar包,並將其封裝到一個List中返回

  3.4 removeDuplicates() 用LinkedHashSet去重,在包裝成ArrayList

  3.5 getExclusions()、checkExcludedClasses()、removeAll()、filter() 過濾注解中要排除的自動配置

  3.6 fireAutoConfigurationImportEvents() 將自動配置導入監聽

  3.7 返回配置的AutoConfigurationEntry包裝類

自動配置原理(簡化版)

通過 @EnableAutoConfiguration 核心注解初始化,並掃描 ClassPath 目錄中自動配置類。根據項目中需要添加的默認配置,如springMVC,就按一定規則獲取默認配置並自動初始化所需要的 Bean。

1. 核心是@EnableAutoConfiguration
2. 里面會通過@Import注解導入(AutoConfigurationImportSelector.class)類
3. 調用importSelect方法,進行自動配置

  3.1 判斷是否開啟了自動配置
  3.2 getAutoConfigurationEntry方法會使用
    SpringFactoriesLoader.loadFactoryNames方法加載classpath中spring.factories文件中EnableAutoConfiguration的配置類
  3.3 經過去重,排除后會把這些配置導入監聽

重點關注

@EnableAutoConfiguration

@Import

AutoConfigurationImportSelector類

selectImports方法

SpringFactoriesLoader.loadFactoryNames() 核心方法讀取 ClassPath 目錄下面的 META-INF/spring.factories 文件。

spring.factories 文件中存放springBoot自動配置類, 如Servlet、jpa

【示例】(JDBC)的DataSourceAutoConfiguration配置

在spring.factories中org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguratio

作用是:SpringBoot 自動配置DataSource

// 表示這是一個配置類
@Configuration(proxyBeanMethods = false)
// 判斷當前項目classpath有沒有這兩個類 DataSource EmbeddedDatabaseType
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
// 將配置文件中對應的值和 DataSourceProperties 綁定起來,並把DataSourceProperties的屬性值放到ioc容器中
@EnableConfigurationProperties(DataSourceProperties.class)
// 導入數據源注冊類、數據源初始化類
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
  // 省略。。
}

查看DataSourceProperties屬性,提供的這些屬性,就可以獲取到在application.yml中提供的值來設置數據源了

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

    private ClassLoader classLoader;

    /**
     * Name of the datasource. Default to "testdb" when using an embedded database.
     */
    private String name;

    /**
     * Whether to generate a random datasource name.
     */
    private boolean generateUniqueName;

    /**
     * Fully qualified name of the connection pool implementation to use. By default, it
     * is auto-detected from the classpath.
     */
    private Class<? extends DataSource> type;

    /**
     * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
     */
    private String driverClassName;

    /**
     * JDBC URL of the database.
     */
    private String url;

    /**
     * Login username of the database.
     */
    private String username;

    /**
     * Login password of the database.
     */
    private String password;

    /**
     * JNDI location of the datasource. Class, url, username & password are ignored when
     * set.
     */
    private String jndiName;

        。。。省略
}

 

總結

Spring Boot 內部提供了很多自動化配置的類, 配置類會更加classpath下是否又相關依賴類,來判斷是否要配置字節,所以當我們在pom中引入了相關依賴,就可以自動配置了

 

======  【多學一點,for Better】======

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM