Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

demo

自己定義一個外部專案,core-bean,依賴如下:

core-beanjarorg。springframeworkspring-context4。3。9。RELEASE

然後定義一個Cat類:

public class Cat {}

定一個配置類:

package core。bean;import org。springframework。context。annotation。Bean;import org。springframework。context。annotation。Configuration;@Configurationpublic class MyConfig {@Beanpublic Cat cat(){return new Cat();}}

我們知道這樣就將Cat類裝配到Spring容器了。

再定義一個springboot專案,加入前面core-bean依賴,依賴如下:

springboot-enableAutoConfigurationjarcom。zhihao。miaocore-bean1。0-SNAPSHOTorg。springframework。bootspring-boot-starter

啟動類啟動:

@EnableAutoConfiguration@ComponentScanpublic class Application {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication。run(Application。class,args);Cat cat = context。getBean(Cat。class);System。out。println(cat);}}

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

發現Cat類並沒有納入到springboot-enableAutoConfiguration專案中。怎麼辦呢。

解決方案,

在core-bean專案resource下新建資料夾META-INF,在資料夾下面新建spring。factories檔案,檔案中配置,key為自定配置類EnableAutoConfiguration的全路徑,value是配置類的全路徑

org。springframework。boot。autoconfigure。EnableAutoConfiguration=core。bean。MyConfig

再啟動springboot-enableAutoConfiguration專案,列印結果:

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

這下啟動正常,在

springboot-enableAutoConfiguration專案中依賴core-bean的jar包以後,自動將core-bean中的配置類載入到了springboot-enableAutoConfiguration的容器中。

在spring各種外掛整合時都用到了該機制,比如tk-Mybatis,可以在其依賴的configuration的jar包中找到對應的配置。這就是spring整合各種外掛的入口。

原理分析

進入EnableAutoConfiguration註解原始碼,發現是匯入EnableAutoConfigurationImportSelector類。

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

跟到最後發現繼承了ImportSelector介面,之前我們講過

Springboot @Enable*註解的工作原理

ImportSelector介面的selectImports返回的陣列(類的全類名)都會被納入到spring容器中。

其在AutoConfigurationImportSelector類中的selectImports實現,進入org。springframework。boot。autoconfigure。AutoConfigurationImportSelector類,

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

進入getCandidateConfigurations方法

getCandidateConfigurations會到classpath下的讀取META-INF/spring。factories檔案的配置,並返回一個字串陣列。

除錯的時候讀取到了core。bean。MyConfig,也讀到了一些其他的配置。

具體的就不細說了,有興趣的朋友可以自己除錯一下。

META-INF/spring。factories還可以配置多個配置類。

比如我們在core-bean下在定義二個類,

package core。bean;public class Dog {}

package core。bean;public class People {}

package core。bean;import org。springframework。context。annotation。Bean;import org。springframework。context。annotation。Configuration;@Configurationpublic class Myconfig2 {@Beanpublic Dog dog(){return new Dog();}}

修改META-INF/spring。factories下的配置,

org。springframework。boot。autoconfigure。EnableAutoConfiguration=core。bean。MyConfig,core。bean。Myconfig2,core。bean。People

修改springboot-enableAutoConfiguration專案的啟動類:

package com。zhihao。miao;import core。bean。Cat;import core。bean。Dog;import core。bean。People;import org。springframework。boot。SpringApplication;import org。springframework。boot。autoconfigure。EnableAutoConfiguration;import org。springframework。context。ConfigurableApplicationContext;import org。springframework。context。annotation。ComponentScan;@EnableAutoConfiguration@ComponentScanpublic class Application {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication。run(Application。class,args);Cat cat = context。getBean(Cat。class);System。out。println(cat);Dog dog = context。getBean(Dog。class);System。out。println(dog);People people = context。getBean(People。class);System。out。println(people);}}

列印結果如下:

Spring SPI機制——搞懂了它也就搞明白了Spring各種外掛的原理

發現都納入到spring容器中了。

可以配置spring。boot。enableautoconfiguration=false禁用自動配置,這樣不會啟動自動配置了,預設是true。還可以排除一些自動配置類,可以在EnableAutoConfiguration註解加入引數,這邊不做過多解釋。

總結,@EnableAutoConfiguration 作用

從classpath中搜索所有META-INF/spring。factories配置檔案然後,將其中org。springframework。boot。autoconfigure。EnableAutoConfiguration key對應的配置項載入到spring容器

只有spring。boot。enableautoconfiguration為true(預設為true)的時候,才啟用自動配置

@EnableAutoConfiguration還可以進行排除,排除方式有2中,一是根據class來排除(exclude),二是根據class name(excludeName)來排除

其內部實現的關鍵點有

1)ImportSelector 該介面的方法的返回值都會被納入到spring容器管理中

2)SpringFactoriesLoader 該類可以從classpath中搜索所有META-INF/spring。factories配置檔案,並讀取配置