javascript
SpringBoot解耦的扩展机制 Spring Factories介绍及使用
一、什么是 SPI機制
Spring Boot中有一種非常解耦的擴展機制:Spring Factories。這種擴展機制實際上是仿照Java中的SPI擴展機制來實現(xiàn)的。SPI的全名為Service Provider Interface.大多數(shù)開發(fā)人員可能不熟悉,因為這個是針對廠商或者插件的。在java.util.ServiceLoader的文檔里有比較詳細的介紹。簡單的總結(jié)下java SPI機制的思想。我們系統(tǒng)里抽象的各個模塊,往往有很多不同的實現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設(shè)計里,我們一般推薦模塊之間基于接口編程,模塊之間不對實現(xiàn)類進行硬編碼。一旦代碼里涉及具體的實現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實現(xiàn),就需要修改代碼。為了實現(xiàn)在模塊裝配的時候能不在程序里動態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機制。java SPI就是提供這樣的一個機制:為某個接口尋找服務(wù)實現(xiàn)的機制。有點類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計中這個機制尤其重要。
二、Spring Boot中的SPI機制
在Spring中也有一種類似與Java SPI的加載機制。它在META-INF/spring.factories文件中配置接口的實現(xiàn)類名稱,然后在程序中讀取這些配置文件并實例化。這種自定義的SPI機制是Spring Boot Starter實現(xiàn)的基礎(chǔ)。
spring.factories
三、Spring Factories實現(xiàn)原理
spring-core包里定義了SpringFactoriesLoader類,這個類實現(xiàn)了檢索META-INF/spring.factories文件,并獲取指定接口的配置的功能。在這個類中定義了兩個對外的方法:
- loadFactories :根據(jù)接口類獲取其實現(xiàn)類的實例,這個方法返回的是對象列表。
- loadFactoryNames :根據(jù)接口獲取其接口類的名稱,這個方法返回的是類名的列表。
上面的兩個方法的關(guān)鍵都是從指定的ClassLoader中獲取spring.factories文件,并解析得到類名列表,具體代碼如下:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?//遍歷整個ClassLoader中所有jar包下的spring.factories文件classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}從代碼中我們可以知道,在這個方法中會遍歷整個ClassLoader中所有jar包下的spring.factories文件。也就是說我們可以在自己的jar中配置spring.factories文件,不會影響到其它地方的配置,也不會被別人的配置覆蓋。spring.factories的是通過Properties解析得到的,所以我們在寫文件中的內(nèi)容都是安裝下面這種方式配置的:
com.xxx.interface=com.xxx.classname如果一個接口希望配置多個實現(xiàn)類,可以使用’,’進行分割。
四、spring-boot包中的spring.factories文件
在Spring Boot的很多包中都能夠找到spring.factories文件,下面就是spring-boot包中的spring.factories文件
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener# Error Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor# Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer# FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter在日常工作中,我們可能需要實現(xiàn)一些SDK或者Spring Boot Starter給被人使用時,我們就可以使用Factories機制。Factories機制可以讓SDK或者Starter的使用只需要很少或者不需要進行配置,只需要在服務(wù)中引入我們的jar包即可。
五、Spring Boot 之spring.factories加載第三方的Bean
這里我們使用Swagger的配置來做實驗。
- 首先一個Swagger的配置類:SwaggerConfig
2:再看我的工程結(jié)構(gòu)吧:
發(fā)現(xiàn)我的SwaggerConfig 類和 SpringBoot 的啟動類ConfigApplication.java 不在同一級目錄下,所以當Spring Boot 自動掃描包的時候,是掃描不到我的SwaggerConfig 的配置的,也就在控制臺沒有Swagger的打印的信息:
所以這時候我如果想要把SwaggerConfig 加載到Spring容器中的話 要怎么辦呢?下面介紹兩種方式
-
①:在Spring Boot Application 主類上 使用@Import 注解
啟動就可以看到Swagger的基礎(chǔ)信息:
-
②:現(xiàn)在我們將其改造一下,采用spring.factories 的方式去加載SwaggerConfig類,在resources目錄下新建一個META-INF 的目錄,然后在新建一個spring.factories 的文件,里面的內(nèi)容為:
然后在把Spring Boot 啟動類上的@Import注釋掉,啟動發(fā)現(xiàn)也可以把SwaggerConfig加載到Spring 容器中
到這就完成了加載一個Spring 不能掃描到的一個類,他可以是第三方的,也可以是自己寫的,只要是Spring Boot 默認掃描路徑不能夠掃描到,都可以使用這種方式去加載。
等配置,掃描指定目錄下Service或Bean.
文章轉(zhuǎn)自
總結(jié)
以上是生活随笔為你收集整理的SpringBoot解耦的扩展机制 Spring Factories介绍及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么需要建设中台?
- 下一篇: 互联网日报 | 1月18日 星期一 |