SpringBoot2.0 自動配置原理

1. 依賴管理

SpringBoot 最方便的地方就是 自動配置互補(bǔ)配置,而自動配置的前提是有這些東西才能配置,也就是必須要有相應(yīng)的依賴。

SpringBoot 有各種各樣的場景啟動器,這些啟動器里邊根據(jù)場景的需求幫我們引入了各種依賴。
所以我們只需要對應(yīng)場景選擇啟動器即可完成大多數(shù)依賴的引入,而且spring-boot-starter-parent => spring-boot-dependencies 里面定義了依賴相應(yīng)的m默認(rèn)版本,但是我們又能夠自定義版本。
各種場景啟動器都會依賴spring-boot-starter,而spring-boot-starter 又會依賴 spring-boot-autoconfigure,后者就是用于自動配置的。

總之:

  1. 場景啟動器里邊配置依賴;
  2. 場景啟動器所依賴的 spring-boot-autoconfigure 幫助自動配置;
  3. 父項(xiàng)目中的 spring-boot-dependencies 管理依賴的版本

2. 自動配置

自動配置的誤區(qū):剛學(xué) springBoot 的時候,認(rèn)為有 @Component、@Configuration 注解的類,程序不就自動注入了么,還管什么自動配置???

這種觀點(diǎn)是片面的,上面的自動注入有一個前提,那就是類需要在掃描包的范圍之內(nèi)(也就是@SpringBootApplication主配置類同級及以下的目錄),但是如果我們引入其他依賴,即使類上面有 @Component 注解,也無法自動注入,因?yàn)樗辉趻呙璋姆秶畠?nèi)。

所以這種情況,就需要借助 springBoot 的自動配置原理,進(jìn)行輔助,完成自動注入。

2.0 幾種自動配置的方式列舉

1)上面說到,無法完成自動注入是因?yàn)椴辉趻呙璋秶畠?nèi),那最直接的方法就是把類納入到掃描范圍

使用 @ComponentScan 增加掃描的范圍

2)@AutoConfiguration + META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

就是 @AutoConfiguration 配置在類上面,然后在 org.springframework.boot.autoconfigure.AutoConfiguration.imports 配置文件中配置這個類的全限定類名。2.7.0 之前版本也兼容

但是實(shí)測,@AutoConfiguration 可以不加,甚至 @Component 也可以不加,只需要 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件配置就行,但如果需要控制加載順序,則可借助 @AutoConfiguration 或者 @AutoConfigureBefore、@AutoConfigureAfter

對于 2.7.0 之前的 springBoot 是另一種方式:@Configuration + META-INF/spring.factories 文件中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 屬性配置全類名(可能意識到需要自動注入的類太多,干脆直接搞一個單獨(dú)維護(hù)自動配置類的文件了:org.springframework.boot.autoconfigure.AutoConfiguration.imports)

3)對于已經(jīng)有 @AutoConfiguration 注解的類,如果某些類依賴于它(也就是這個類注入了,某些類注入才有意義),或者這個類依賴某些其他類(例如注入的成員變量),這種情況可以借助 @Import 注解

有三種方式:
1.直接在 @Import 的 value 中加入依賴或被依賴的類
2.自定義導(dǎo)入類實(shí)現(xiàn) ImportSelector 接口,并加入到 @Import 的 value 中
3.自定義導(dǎo)入類實(shí)現(xiàn) ImportBeanDefinitionRegistrar 接口,并加入到 @Import 的 value 中
后兩種方法,適用于較多的情況。

值得注意的是,如果沒有指定順序(可以使用@Component + @Order), @Import 是按照 value 數(shù)組的順序注入的

4)原生 servlet 注入

@ImportResource 用于指定 xml 位置,用于自動注入

5)對于配置類

這種情況可以使用:@ConfigurationProperties(放在配置類上) + @EnableConfigurationProperties(放在自動注入的類上)

2.1 關(guān)于容器注入和屬性綁定的幾種注解

1) @Configuration + @Bean

@Configuration 標(biāo)注在配置類上,聲明這個類是一個配置類,本質(zhì)上也是容器中的組件(@Component)。其中一個屬性proxyBeanMethods,默認(rèn)為true,含義是是否需要代理,就是是否為單例,true 代表單例,false 代表不是單例。

@Bean 標(biāo)注在配置類方法上,用于給容器中注入組件,類型為方法返回值類型,組件名稱默認(rèn)為方法名,也可以自定義組件名。

2)@Import

標(biāo)注在類上,也是為容器中注入組件,前提是該類也是容器中的一個組件(@Configuration,@Component 等等都實(shí)行),屬性 value 是一個 Class 數(shù)組,用于指定注入哪些類型的組件,組件名和類同名但首字母小寫。
通常直接在 @Import 的 value 中指定需要導(dǎo)入的類型即可,但有的時候類型太多或者不固定,就不適合直接寫導(dǎo)入的類了,可以用下面兩種方法。

a. 借助 ImportSelector

實(shí)現(xiàn) ImportSelector 接口,它有一個 selectImports 方法,用于返回想要導(dǎo)入的容器的全類名字符串?dāng)?shù)組。使用時只要把這個 Selector 填入 @Import 即可借助 @Import 將這些類注入

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}
// demo
public class DemoSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                Demo.class.getName() // 需要導(dǎo)入的類都加入到這個數(shù)組即可
        };
    }
}
b. 借助 ImportBeanDefinitionRegistrar

實(shí)現(xiàn) ImportBeanDefinitionRegistrar,實(shí)現(xiàn) registerBeanDefinitions 方法,利用 registry.registerBeanDefinition() 將想要注入的組件注冊,然后將這個 Registar 填入 @Import value 中即可。
ImportSelector 中按照全限定類名注入,中間對于bean沒有什么操作空間。相對于實(shí)現(xiàn) ImportSelector,實(shí)現(xiàn) ImportBeanDefinitionRegistrar 稍微麻煩一點(diǎn),但是注入bean之前可以做更多事。

public interface ImportBeanDefinitionRegistrar {
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        this.registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}
//例子
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //想要住放入的類,需要通過 BeanDefinition 包裝一下,
        RootBeanDefinition root = new RootBeanDefinition(Test.class);
        registry.registerBeanDefinition("組件名", root);
    }
}

3)@Conditional

標(biāo)注與配置類或組件上面(@Configuration, @Bean...),用于條件裝配,滿足一定條件才注入,


image.png

4)@ImportResource

用于引入原生的配置文件。如 XXX.xml 這種形式的文件,里面注入的組件,SpringBoot 是不能直接獲取的,利用這個注解,可以使其生效,屬性 locations 是配置文件路徑的字符數(shù)組。

@ImportResource("classpath:beans.xml")

5)@ConfigurationProperties,@Value

@Value 標(biāo)注在屬性上,從配置文件中讀取數(shù)據(jù)賦值給屬性@ConfigurationProperties 標(biāo)注類上,可以從配置文件中讀取信息,綁定到類的屬性中,使其生效的方法有兩種:

a. 標(biāo)注在組件上

也就是說這個類不止要被 @ConfigurationProperties 修飾,還要是一個組件(@Configuration,@Component...)。

b. 配合 @EnableConfigurationProperties

在某個配置類上,用 @EnableConfigurationProperties 標(biāo)注,并把 @ConfigurationProperties 標(biāo)注的類的 class 填入到 @EnableConfigurationProperties 的屬性中即可。
這個注解有兩個作用:
1、開啟 @ConfigurationProperties 標(biāo)注類的配置綁定功能
2、把這個類的組件自動注冊到容器中

6)@ComponentScan

用來指定 Spring 掃描的包,也就是掃描范圍,默認(rèn)是 @Component 注解所在的同級及以下目錄

2.2 自動注入的關(guān)鍵,@SpringBootApplication

//說明@SpringBootApplication也是個配置類
@SpringBootConfiguration
//開啟自動配置
@EnableAutoConfiguration
//包掃描
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@ComponentScan 在不指定掃描路徑的情況下,默認(rèn)掃描標(biāo)注類的同級及以下的路徑??梢酝ㄟ^ @SpringBootApplication 的 scanBasePages 屬性自定義掃描路徑。

//自動配置包
@AutoConfigurationPackage
//導(dǎo)入一個 Selector
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

導(dǎo)入了一個 AutoConfigurationImportSelector 組件,這個組件會總 "META-INF/spring.factories" 文件中讀取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,里邊是各種 XXXAutoConfiguration的全類名。所以它就是用來幫我們導(dǎo)入大量的配置類的。

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

這里又導(dǎo)入了一個 AutoConfigurationPackages.Registrar,這個大致就是為當(dāng)前項(xiàng)目包下的所有組件,如果配置了 scanBasePackages 就用配置的包,沒有配置就用主配置類所在的包。

總結(jié):

@SpringBootApplication 幫我們開啟了自動配置和組件導(dǎo)入。
借助 @Import 和 AutoConfigurationImportSelector 導(dǎo)入各種 AutoConfiguration 組件,用于自動配置。
借助 @Import 和 AutoConfigurationPackages.Registrar 導(dǎo)入當(dāng)前項(xiàng)目包路徑中的組件。

3. 自動配置

@SpringBootApplication 從 spring-boot-autoconfigure 中獲取并注入各種 AutoConfiguration 組件,用來自動配置,里邊會利用 2.1 中的幾種注解實(shí)現(xiàn)自動配置,互補(bǔ)配制和條件裝配。例如:Servlet 容器自動配置的例子解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容