SpringBoot自動裝配原理

Springboot自動裝配過程

什么是Springboot自動裝配?
我們使用ssm開發(fā)的時候,需要寫spring.xml或者配置bean,需要配置<bean/>、@Bean把類交給工廠管理。而反觀springboot開發(fā)的時候,只需要配置一些簡單的.properties(.yml)就行了。其實springboot就是把這些我們以前要寫的<bean/>、@Bean封裝成啟動器(starter)導(dǎo)入交給工廠了。

需要了解springboot的自動裝配過程,必須從如下幾個相關(guān)注解開始

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

@Target({ElementType.TYPE}):注解可以標(biāo)記在哪;
@Retention(RetentionPolicy.RUNTIME):注解標(biāo)注的類編譯以什么方式保留;
@Documented:生成文檔信息的時候保留注解,對類作輔助說明;
@Inherited:繼承關(guān)系;
@SpringBootConfiguration:Spring Boot的配置類;
@EnableAutoConfiguration:開啟自動配置功能;
@ComponentScan(包路徑):掃描包;
excludeFilters:排除策略;

如:@ComponentScan(basePackages = "com.not_lsj",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.not_lsj.controller.*"})})
掃描 com.not_lsj 包,但是把controller包下所有的類都排除掉,不參與掃描
  • @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

}

@AutoConfigurationPackage:將當(dāng)前配置類所在包保存在BasePackages的Bean中,供Spring內(nèi)部使用。就是注冊了一個bean,這個bean用來保存當(dāng)前配置類的;
@Import({AutoConfigurationImportSelector.class}):導(dǎo)入當(dāng)前類;

  • Conditional衍生注解
    作用:條件注解,必須是@Conditional指定的條件成立,才給容器中添加組件,組件才生效。
    列舉幾種常見的:

@ConditionalOnBean:容器中存在指定Bean
@ConditionalOnMissingBean:容器中不存在指定Bean
@ConditionalOnClass:系統(tǒng)中有指定的類
@ConditionalOnMissingClass:系統(tǒng)中沒有指定的類
@ConditionalOnProperty:系統(tǒng)中指定的屬性是否有指定的值
@ConditionalOnWebApplication:當(dāng)前是web環(huán)境
......

順便說一下,spring創(chuàng)建bean的幾種方式:

  1. XML解析
  2. 注解(@Configuration、@Component(及其衍生注解)、@Bean)
  3. ClassPathBeanDefinitionScanner掃描特定路徑下的具有注解的Bean 進(jìn)行注冊
  4. @import

在此介紹一下@Import注解:作用是導(dǎo)入某個類(一般用于封裝)

A.class是一個普通類

  1. @Import(A.class):直接導(dǎo)入某個類

B implements importSelector

  1. @Import(B.class):一次導(dǎo)入多個類
    importSelector有一個子接口 DeferredImportSelector

C implements importBeanDefinitionRegister

  1. @Import(C.class):導(dǎo)入一個定制的類

Springboot就是用的@Import注解實現(xiàn)自動配置,實現(xiàn)了DeferredImportSelector接口

  • DeferredImportSelector

有兩大特性

  1. 延遲加載:spring源碼中解析@Import注解,都是再其他注解解析完后。
    如:某個Bean在容器中通過@Bean注冊了,我們需要定制同一個Bean,這時就需要延遲加載了,因為會通過@ConditionOnBean..校驗當(dāng)前容器是否存在這個Bean,如果存在,我們定制的就不生效。
  2. 分組:當(dāng)前類導(dǎo)入的Bean,都是與Spring容器里面的Bean分開來的,排序只是在本組內(nèi);
    如:當(dāng)前spring自己也有很多Bean要注冊,然后我們通過DeferredImportSelector又導(dǎo)入一組Bean,這兩組Bean時互補(bǔ)干擾的,相反如果他們沒有分組,會導(dǎo)致容器的與我們定制的不能保證加載順序,上訴的@ConditionOnBean..校驗會存在問題;如果先加載我們定制的Bean,而到容器加載的時候,發(fā)現(xiàn)存在這個Bean就會報錯。

實現(xiàn)類的方法執(zhí)行順序:
先調(diào)用getImportGroup()
有返回值:執(zhí)行返回值的內(nèi)容 -->process() -->selectImports(),會實現(xiàn)排序
無返回值:執(zhí)行父類的selectImports()導(dǎo)入Bean

  • 部分實現(xiàn)的源碼
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
        Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
            return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
        });
        // getAutoConfigurationEntry進(jìn)行掃描具有META-INF/spring.factories文件的jar
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
        this.autoConfigurationEntries.add(autoConfigurationEntry);
        Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

        while(var4.hasNext()) {
            String importClassName = (String)var4.next();
            this.entries.putIfAbsent(importClassName, annotationMetadata);
        }

    }

通過@Import導(dǎo)入一個DeferredImportSelector類型的類:AutoConfigurationImportSelector

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            // 獲取EnableAutoConfiguration注解屬性信息
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 從META‐INF/spring.factories中獲得候選的自動配置類(?。。。?            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            // 排重
            configurations = this.removeDuplicates(configurations);
            //根據(jù)EnableAutoConfiguration注解中屬性,獲取不需要自動裝配的類名單
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            // 根據(jù):@EnableAutoConfiguration.exclude 
            //      @EnableAutoConfiguration.excludeName 
            //      spring.autoconfigure.exclude 進(jìn)行排除
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            // 通過讀取spring.factories 中的OnBeanCondition\OnClassCondition\OnWebApplicationCondition進(jìn)行過濾
            configurations = this.getConfigurationClassFilter().filter(configurations);
            // 這個方法是調(diào)用實現(xiàn)了AutoConfigurationImportListener的bean.. 分別把候選的配置名單,和排除的配置名單傳進(jìn)去做擴(kuò)展
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }
// 最后排序并注冊Bean
 public Iterable<Entry> selectImports() {}

每一個springboot的應(yīng)用,都會導(dǎo)入一個spring-boot-autoconfigure模塊,而META-INF/spring.factories文件就在這個模塊下面。而校驗自動裝配生效的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,如圖:

2b91dcce8d716cd5275b90d65472c47.png

說明:在springboot啟動類內(nèi)部執(zhí)行run時,會執(zhí)行selectImports()方法,找到對應(yīng)的配置類的權(quán)限定名對應(yīng)的class,加載到spring容器中。

最后看個例子:上面截圖的AopAutoConfiguration

@Configuration(
    proxyBeanMethods = false // @Configuration表示是一個配置類, proxyBeanMethods = false表示當(dāng)前類不是代理類(默認(rèn)true)
)
@ConditionalOnProperty(  // 表示當(dāng)前項目環(huán)境必須存在某種配置
    prefix = "spring.aop",
    name = {"auto"},  // 必須spring.aop.auto配置
    havingValue = "true",
    matchIfMissing = true // 如果等于false,不存在spring.aop.auto,aop不生效                     
) // 但是這里等于true表示不存在spring.aop.auto配置,就是用默認(rèn)的aop
public class AopAutoConfiguration {

}

pom.xml引入xxx-starter,自動裝配xxx功能,根據(jù)定義的規(guī)則校驗成功之后,就是將所需的類導(dǎo)入spring工廠管理,完成自動裝配。

  • 流程圖
    springboot-自動裝配.png

總結(jié):

  1. 首先通過@Import導(dǎo)入一個DeferredImportSelector類型的類
  2. 掃描所有jar中的META‐INF/spring.factories
  3. 得到所有的有類路徑的Bean組成List進(jìn)行排序加載
  4. 最后spring就能管理這些類了
  • 自定義啟動器(starter)

通過對springboot自動配置原理的了解,只要根據(jù)springboot規(guī)則,通過org.springframework.boot.autoconfigure.EnableAutoConfiguration導(dǎo)入,就能制定我們自定義的starter:
規(guī)則:
啟動器(starter)就是空的jar文件,用來提供輔助性依賴管理。
自定義一個spring-boot-autoconfigure的配置模塊,寫配置類
命名:
官方:spring-boot-starter-xxx
非官方:xxx-spring-boot-starter(mybatis)

實現(xiàn)步驟:

構(gòu)建一個管理父項目(類是微服務(wù)的父項目)
構(gòu)建xxx-spring-boot-autoconfigure模塊實現(xiàn)配置類
構(gòu)建xxx-spring-boot-starter提供調(diào)用

自定義starter案例:https://gitee.com/not-lsj/spring-boot-starter.git

結(jié)尾:

  1. 本文介紹了springboot自動裝配原理
  2. 模擬實現(xiàn)了自定義starter
  3. 某個時刻會把springboot啟動原理總結(jié)發(fā)布和兩篇文章的總結(jié)流程圖奉上
  4. 歡迎來交流

最后的最后:第一次寫總結(jié),可能會不太好,但是會越來越努力!??!

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

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

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