http://www.cnblogs.com/supermaskv/p/12464830.html

Spring Boot 自動裝配流程

本文以 mybatis-spring-boot-starter 爲例簡單分析 Spring Boot 的自動裝配流程。java

Spring Boot 發現自動配置類

這裏說的自動配置類指的是在 META-INF/spring.factories 文件中聲明的 XXXAutoConfiguration 類。web

首先,咱們從 @SpringBootApplication 註解的定義中,咱們能夠發現有一個叫作 @EnableAutoConfiguration 的註解,這也是 SpringBoot 實現自動裝配最關鍵的註解。spring

//@EnableAutoConfiguration 註解的定義@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 {};
}

@Target @Retention @Documented @Inherited 都是 jdk 提供的註解,有興趣能夠去查查看,這裏就不作分析了。這裏主要分析 @AutoConfigurationPackage 和 @Import({AutoConfigurationImportSelector.class}) 究竟起到什麼做用。mybatis

//@AutoConfigurationPackage 註解的定義@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

在 @AutoConfigurationPackage 註解的定義中,咱們又發現一個 @Import 註解。 @Import 註解是由 Spring 提供的,做用是將某個類實例化並加入到 Spring IoC 容器中。因此咱們要想知道 @Import({Registrar.class}) 究竟作了什麼就須要瞭解 Registrar 這個類裏包含了哪些方法。less

//Registrar 類的定義static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
    }    public Set<Object> determineImports(AnnotationMetadata metadata) {        return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    }
}

Registrar 類裏一共有兩個方法,分別是 determineImports 和 registerBeanDefinitions 。ide

determineImports 方法在個人項目的啓動過程當中並無觸發斷點,官方的文檔描述這個方法返回的是一組表明要導入項的對象。spring-boot

registerBeanDefinitions 方法觸發斷點後發現工具

new AutoConfigurationPackages.PackageImport(metadata)).getPackageName() 方法返回的就是 @SpringBootApplication 註解所在的類的包名。this

因此 @AutoConfigurationPackage 註解的做用應該是掃描與 @SpringBootApplication 標註的類同一包下的全部組件。spa

瞭解了 @AutoConfigurationPackage 註解後,咱們回到 @EnableAutoConfiguration 的定義,還有一個 @Import({AutoConfigurationImportSelector.class}) 須要咱們瞭解。 AutoConfigurationImportSelector 類定義的內容不少,咱們着重瞭解其中一個重要的方法

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

這個方法中,除了 loadMetadata 獲取註解元數據以外,就是 getAutoConfigurationEntry 獲取自動配置條目。

//getAutoConfigurationEntry 的定義protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {    if (!this.isEnabled(annotationMetadata)) {        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        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 的方法中,這個方法名告訴咱們這個方法的做用是獲取候選配置。

//getCandidateConfigurations 的定義protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {    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;
}

從這個方法中的 Assert.notEmpty() 中咱們能夠反推得出,Spring Boot 除了掃描本身 jar 包中 META-INF/spring.factories 以外,還會去找別的 jar 包中是否存在 META-INF/spring.factories 。這也爲第三方開發本身的 spring-boot-starter 提供了便利。

@Conditional 系列註解

在研究 mybatis-spring-boot-starter 以前,咱們還須要瞭解一下 Spring 爲提供的強大的 @Conditional 系列註解。

iMBjEf7.png!web

@Conditional擴展註解 做用(判斷當前條件是否知足)
@ConditionalOnJava 系統的java版本是否符合要求
@ConditionalOnBean 容器中是否存在指定的Bean
@ConditionalOnMissingBean 容器中不存在指定的類
@ConditionalOnExpression 知足SpEL表達式指定規範
@ConditionalOnClass 在系統中有指定的對應的類
@ConditionalOnMissingClass 在系統中沒有指定對應的類
@ConditionalOnSingleCandidate 容器中是否指定一個單實例的Bean,或者找個是一個首選的Bean
@ConditionalOnProperty 系統中指定的對應的屬性是否有對應的值
@ConditionalOnResource 類路徑下是否存在指定的資源文件
@ConditionalOnWebApplication 當前是Web環境
@ConditionalOnNotWebApplication 當前不是Web環境
@ConditionalOnJndi JNDI存在指定項

表格中的系統指的是項目自己,而非操做系統。

mybatis-spring-boot-starter

在 mybatis-spring-boot-starter 中,咱們能夠看到內容不多,僅有一個 pom.xml 文件用於引入依賴,因此 mybatis-spring-boot-starter 並不直接起做用,而是利用其它依賴完成自動配置。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
    </dependency></dependencies>

咱們能夠發現 mybatis-spring-boot-starter 的依賴項中有一個叫作 mybatis-spring-boot-autoconfigure 的依賴項。這頗有可能就是 mybatis 對本身完成自動裝配真正起做用的工具。

yYbABzI.png!web

果真在 mybatis-spring-boot-autoconfigure 的 META-INF 中咱們找到了 spring.factories 文件。

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

MybatisLanguageDriverAutoConfiguration 類是對各類語言的支持,如 Thymeleaf 、 FreeMarker 等。配置 Mybatis 核心組件的是 MybatisAutoConfiguration 類。

//MybatisAutoConfiguration 類定義@Configuration@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties({MybatisProperties.class})@AutoConfigureAfter({DataSourceAutoConfiguration.class,
                     MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {    //....}

首先 MybatisAutoConfiguration 類是一個被 @Configuration 標記了的配置類。這不難理解, MybatisAutoConfiguration 類會爲 Mybatis 配置一些關鍵的 Bean 並加入到容器中去。

接着就是兩個 @Conditional 系列的註解,表示當項目中存在 SqlSessionFactory.class 與 SqlSessionFactoryBean.class 而且存在 DataSource.class 的單例實例或者首選實例時, MybatisAutoConfiguration 纔會被加入到容器中去。

@EnableConfigurationProperties({MybatisProperties.class}) 這個註解的做用是將配置文件( .propertyies 和 .yml)與 MybatisProperties 類關聯起來,也就是說這個註解能讓 Spring Boot 從配置文件中讀取數據。

@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) 這個註解的做用也很是明顯,就是要求 Spring Boot 在裝配 MybatisAutoConfiguration 以前先完成 DataSourceAutoConfiguration 和 MybatisLanguageDriverAutoConfiguration 的裝配。這樣能夠保證 Mybatis 在裝配時,全部的依賴項都已經到位。

除了 MybatisAutoConfiguration 自己以外,類中也定義了一些按條件生成的 Bean,保證 Mybatis 能在各類條件下成功的自動裝配。

總結

  1. Spring Boot 在啓動時除了掃描與啓動類同一包下的組件以外,還會檢查各個 jar 包中是否存在 META-INF/spring.factories 文件,爲自動裝配作準備。

  2. 第三方的 spring-boot-starter 會經過將本身的自動裝配類寫到 META-INF/spring.factories 中讓 Spring Boot 加載到容器中,使自動裝配類可以生效。

  3. 第三方的自動裝配類會經過利用 @Conditional 系列註釋保證本身能在各類環境中成功自動裝配。

相關文章
相關標籤/搜索