Spring-boot @EnableAutoConfiguration源碼分析

閱讀本文前,請(qǐng)先閱讀作者的另一篇文章Spring-Boot之@Enable*注解的工作原理

@EnableAutoConfiguration作用:從classpath中搜索所有的META-INF/spring.factories配置文件,然后將其中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的value加載到spring容器中

首先我們來做一個(gè)小實(shí)驗(yàn):
1、創(chuàng)建一個(gè)spring項(xiàng)目jianshu-starter

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bamu.jianshu</groupId>
    <artifactId>jianshu-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>jianshu-starter</name>
    <description>The first Spring Boot project</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、編寫configuration類,注入到spring容器中

@Configuration
public class RunnableConfiguration {
    @Bean
    public Runnable runnable() {
        return () -> {};
    }
}

3、創(chuàng)建一個(gè)spring-boot項(xiàng)目blog,引入剛剛創(chuàng)建的外部項(xiàng)目jianshu-starter

//...
<dependency>
      <groupId>com.bamu.jianshu</groupId>
      <artifactId>jianshu-starter</artifactId>
      <version>1.0-SNAPSHOT</version>
</dependency>
//...

4、在blog項(xiàng)目中編寫啟動(dòng)類BlogApplication

@SpringBootApplication
public class BlogApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(BlogApplication.class, args);
        //獲取jianshu-starter項(xiàng)目中定義的bean runnable
        System.out.println(context.getBean("runnable").getClass().getName());
        context.close();
    }
}

5、啟動(dòng)

console result

咦?@SpringBootApplication注解中不是為我們配置了@EnableAutoConfiguration了嗎?為什么這里獲取不到外部項(xiàng)目注入到spring容器中的bean呢?

為了解答這個(gè)疑惑,我們查閱@EnableAutoConfiguration注解中import進(jìn)去的Selector的源碼,追溯到AutoConfigurationImportSelector這個(gè)類后,我們發(fā)現(xiàn)了getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)這個(gè)方法。

getCandidateConfigurations方法

紅框中的英文翻譯后意思為:在META-INF / spring.factories中找不到自動(dòng)配置類。 如果您使用自定義打包,請(qǐng)確保文件正確無誤。

繼續(xù)帶著疑惑,我們?cè)囍c(diǎn)進(jìn)loadFactoryNames這個(gè)方法中去一探究竟:

loadFactoryNames方法

噢,原來,這個(gè)方法通過是ClassLoader,去我們的classpath目錄下的META-INF/spring.factoried文件中去查找以EnableAutoConfiguration這個(gè)類的全類名為key的值,然后把它們add到一個(gè)集合當(dāng)中return出去。看完這個(gè)方法的源碼后,我們回到上一個(gè)截圖getCandidateConfigurations方法的源碼,發(fā)現(xiàn)他在做了一個(gè)非空校驗(yàn)后,接著往上層返回。那我們找到調(diào)用getCandidateConfigurations方法的代碼看看。

selectImports方法

我們追溯到了selectImports這個(gè)方法。閱讀過Spring-Boot之@Enable*注解的工作原理這篇文章的讀者肯定一下就看明白了,噢,原來這個(gè)方法獲取到了List<String> configurations這個(gè)集合后,通過removeDuplicates方法做了去重、通過sort方法做了排序、通過getExclusions這個(gè)方法做了排除(那我們排除的依據(jù)從哪來呢?大家可以打開@EnableAutoConfiguration注解的源碼一看便知,該注解給我們提供了兩種方式排除我們不想注入到spring容器中的bean)......最后將configurations這個(gè)集合轉(zhuǎn)為String[]返回,通過推薦的文章我們知道,selectImports()方法返回出去的數(shù)組是會(huì)被spring容器托管的,這下我們?nèi)懒耍?br> 在springboot項(xiàng)目中,其他包下自動(dòng)裝配的bean,是需要在classpath下的META-INF/spring.factories文件中配置,才能被spring容器注入

so,我們按照springboot的要求,創(chuàng)建一個(gè)spring.factories并配置我們需要獲取的bean runnable

spring.factories

META-INF/spring.factories

提醒一下:loadFactoryNamesselectImports方法的源碼我們發(fā)現(xiàn),key必須是EnableAutoConfiguration注解的全類名,value必須是你想自動(dòng)裝配的bean的全類名
再執(zhí)行BlogApplication類試一試:

console result

完美地獲取到了runnable這個(gè)對(duì)象

另外,拓展一下springboot為我們提供的starter包如何實(shí)現(xiàn)開箱即用。事實(shí)上,我們看到的springboot提供的starter包,都引用了很多我們比較熟悉的jar包,它也是通過我在上文中介紹的方式,將它想要注入的bean,配置到spring.factories文件中。例如:

spring-boot-autoconfiguration

我們打開spring.factories文件看一看

spring.properties文件冰山一角

看看,spring-boot-autoconfiguration這個(gè)項(xiàng)目注入了這么多其他項(xiàng)目的bean。

以上,就是Spring-boot @EnableAutoConfiguration源碼分析的全部?jī)?nèi)容,望讀者帶著批判的心態(tài)閱讀我的文章,發(fā)現(xiàn)錯(cuò)誤,麻煩直接評(píng)論,共同學(xué)習(xí),共同進(jìn)步。
也可以觀看spring-boot系列的其他文章,都是干貨喲~

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

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

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