javascript
Spring Boot 扩展点应用之工厂加载机制
Spring 工廠加載機制,即 Spring Factories Loader,核心邏輯是使用 SpringFactoriesLoader 加載由用戶實現的類,并配置在約定好的META-INF/spring.factories 路徑下,該機制可以為框架上下文動態的增加擴展。
該機制類似于 Java SPI,給用戶提供可擴展的鉤子,從而達到對框架的自定義擴展功能。
核心實現類 SpringFactoriesLoader
SpringFactoriesLoader 是 Spring 工廠加載機制的核心底層實現類。它的主要作用是 從 META-INF/spring.factories 路徑下加載指定接口的實現類。該文件可能存在于工程類路徑下或者 jar 包之內,所以會存在多個該文件。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";SpringFactoriesLoader loadFactories load 并從 FACTORIES_RESOURCE_LOCATION文件中實例化給定類型的工廠實現類。 spring.factories 文件必須采用 Properties 格式,其中鍵是接口或抽象類的完全限定*名稱,值是逗號分隔的實現類名稱列表。例如:
該文件的格式,Key 必須為接口或抽象類的全限定名,value 為 具體的實現類,多個以 逗號分隔。類似如下配置:
# 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從該文件中我們可以看到,其中 ApplicationContextInitializer 為父類,value為實現類,以逗號分隔。
SpringFactoriesLoader 源碼分析
Spring Boot 完成自動裝配的核心之一就是工廠加載機制。我們以 Spring Boot 的自動裝配為例來分析。如果要開啟 Spring 的自動裝配功能,會使用 @EnableAutoConfiguration 這個注解,這個注解會 Import AutoConfigurationImportSelector 這個類。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {AutoConfigurationImportSelector 中有一個方法就是加載 EnableAutoConfiguration 為 key 的實現配置類。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,this.beanClassLoader) }SpringFactoriesLoader loadFactories 加載 所有以 factoryClass 為 Key 的實現類
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {// 省略部分前置判斷和 logger 代碼 ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}//根據當前接口類的全限定名作為key,從loadFactoryNames從文件中獲取到所有實現類的全限定名List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);List<T> result = new ArrayList<>(factoryNames.size());//實例化所有實現類,并保存到 result 中返回。for (String factoryName : factoryNames) {result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;}調用 loadSpringFactories 從META-INF/spring.factories文件中進行加載
從文件中讀取接口和實現類的邏輯,返回 Map<String, List<String>>
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {//FACTORIES_RESOURCE_LOCATION --> META-INF/spring.factoriesEnumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));//一Key多值 Map,適合上文提到的一個接口多個實現類的情形。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()) {//以逗號進行分割,得到List的實現類全限定名集合List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));result.addAll((String) entry.getKey(), factoryClassNames);}}cache.put(classLoader, result);//返回return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);} }總結
上面通過以 Spring Boot 的自動裝配為例,我們分析了 Spring 工廠加載機制的整個過程,重點分析了SpringFactoriesLoader類。通過這樣的機制,我們可以十分的方便的為框架提供各式各樣的擴展插件,我們可以自己定義自己的組件的自動裝配配置類,然后通過工廠加載機制讓 Spring 進行加載并得到自動裝配。
工廠加載機制的應用 ApplicationContextInitializer
ApplicationContextInitializer 是在 Spring Boot 或者 Spring Mvc 啟動過程中調用的。具體時機為Spring 應用上下文 refresh 之前(調用 refresh 方法)。
ApplicationContextInitializer 主要提供應用上下文未refresh之前的擴展,這時候可以對 ConfigurableApplicationContext 進行一些擴展處理等。
自定義一個類,實現 ApplicationContextInitializer ,并重寫 initialize 方法:
@Order(Ordered.HIGHEST_PRECEDENCE) public class HelloWorldApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("=================> applicationContext: " + applicationContext.getId());} }啟動 Spring Boot 程序,我們可以看到在 refresh 之前,會在控制臺打印上面這句話。
- 另外的實現方式:
- application.properties添加配置方式:
對于這種方式是通過 DelegatingApplicationContextInitializer 這個初始化類中的 initialize 方法獲取到 application.properties 中 context.initializer.classes 對應的實現類,并對該實現類進行加載。
3.在 SpringApplication 中直接添加
public static void main(String[] args) {new SpringApplicationBuilder(SpringBootDemo3Application.class).initializers(new AfterHelloWorldApplicationContextInitializer()).run(args);} }Spring Boot 使用 工廠機制加載 ApplicationListener 實現類
# 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總結
工廠加載機制是 Spring 動態加載實現類的一種方式,提前在擴展類中寫好對應自動配置類,我們可以完成自動裝配的效果。Spring Boot 自動裝配模塊其中的loader 自動配置實現類就是基于此實現的。
Spring Boot 的一些新特性幾乎用到的都是 Spring Framework 的核心特性。因此學習 Spring Boot ,歸根結底就是學習 Spring Framework 核心。它是所有 Spring 應用的基石,所以我們應該從上至下,由淺入深來進行學習和分析。
轉載于:https://www.cnblogs.com/leihuazhe/p/9751836.html
總結
以上是生活随笔為你收集整理的Spring Boot 扩展点应用之工厂加载机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建行龙卡贷吧申请被拒是怎么回事?龙卡贷吧
- 下一篇: Linux 删除用户时报错:userde