Spring
工廠加載機制鸟蟹,即Spring Factories Loader
蜕提,核心邏輯是使用SpringFactoriesLoader
加載由用戶實現(xiàn)的類权旷,并配置在約定好的META-INF/spring.factories
路徑下,該機制可以為框架上下文動態(tài)的增加擴展贯溅。
該機制類似于Java SPI
,給用戶提供可擴展的鉤子躲查,從而達到對框架的自定義擴展功能它浅。
核心實現(xiàn)類 SpringFactoriesLoader
SpringFactoriesLoader
是Spring
工廠加載機制的核心底層實現(xiàn)類。它的主要作用是 從META-INF/spring.factories
路徑下加載指定接口的實現(xiàn)類镣煮。該文件可能存在于工程類路徑下或者 jar 包之內(nèi)姐霍,所以會存在多個該文件。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
SpringFactoriesLoader
loadFactories
load 并從 FACTORIES_RESOURCE_LOCATION
文件中實例化給定類型的工廠實現(xiàn)類。 spring.factories
文件必須采用 Properties
格式镊折,其中鍵是接口或抽象類的完全限定*名稱胯府,值是逗號分隔的實現(xiàn)類名稱列表。例如:
該文件的格式恨胚,Key
必須為接口或抽象類的全限定名骂因,value
為 具體的實現(xiàn)類,多個以 逗號分隔赃泡。類似如下配置:
# 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為實現(xiàn)類升熊,以逗號分隔俄烁。
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
的實現(xiàn)配置類辰企。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
this.beanClassLoader)
}
SpringFactoriesLoader
loadFactories
加載 所有以 factoryClass
為 Key
的實現(xiàn)類
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
// 省略部分前置判斷和 logger 代碼
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//根據(jù)當前接口類的全限定名作為key,從loadFactoryNames從文件中獲取到所有實現(xiàn)類的全限定名
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
List<T> result = new ArrayList<>(factoryNames.size());
//實例化所有實現(xiàn)類渊抽,并保存到 result 中返回蟆豫。
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
調(diào)用 loadSpringFactories
從META-INF/spring.factories
文件中進行加載
從文件中讀取接口和實現(xiàn)類的邏輯,返回
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.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
//一Key多值 Map懒闷,適合上文提到的一個接口多個實現(xiàn)類的情形十减。
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的實現(xiàn)類全限定名集合
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);
}
}
總結(jié)
上面通過以 Spring Boot
的自動裝配為例愤估,我們分析了 Spring
工廠加載機制的整個過程帮辟,重點分析了SpringFactoriesLoader
類。通過這樣的機制玩焰,我們可以十分的方便的為框架提供各式各樣的擴展插件由驹,我們可以自己定義自己的組件的自動裝配配置類,然后通過工廠加載機制讓 Spring
進行加載并得到自動裝配昔园。
工廠加載機制的應(yīng)用 ApplicationContextInitializer
ApplicationContextInitializer
是在Spring Boot
或者Spring Mvc
啟動過程中調(diào)用的蔓榄。具體時機為Spring 應(yīng)用上下文refresh
之前(調(diào)用refresh
方法)。
ApplicationContextInitializer 主要提供應(yīng)用上下文未refresh之前的擴展默刚,這時候可以對 ConfigurableApplicationContext
進行一些擴展處理等甥郑。
自定義一個類,實現(xiàn) ApplicationContextInitializer
荤西,并重寫 initialize
方法:
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("=================> applicationContext: " + applicationContext.getId());
}
}
啟動 Spring Boot
程序澜搅,我們可以看到在 refresh
之前伍俘,會在控制臺打印上面這句話。
- 另外的實現(xiàn)方式:
- application.properties添加配置方式:
context.initializer.classes=com.maple.spring.initializer.AfterHelloWorldApplicationContextInitializer
對于這種方式是通過 DelegatingApplicationContextInitializer
這個初始化類中的 initialize
方法獲取到 application.properties
中 context.initializer.classes
對應(yīng)的實現(xiàn)類勉躺,并對該實現(xiàn)類進行加載癌瘾。
3.在 SpringApplication
中直接添加
public static void main(String[] args) {
new SpringApplicationBuilder(SpringBootDemo3Application.class)
.initializers(new AfterHelloWorldApplicationContextInitializer())
.run(args);
}
}
Spring Boot 使用 工廠機制加載 ApplicationListener 實現(xiàn)類
# 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
總結(jié)
工廠加載機制是 Spring
動態(tài)加載實現(xiàn)類的一種方式,提前在擴展類中寫好對應(yīng)自動配置類饵溅,我們可以完成自動裝配的效果妨退。Spring Boot
自動裝配模塊其中的loader 自動配置實現(xiàn)類就是基于此實現(xiàn)的。
Spring Boot
的一些新特性幾乎用到的都是 Spring Framework
的核心特性概说。因此學(xué)習 Spring Boot
碧注,歸根結(jié)底就是學(xué)習 Spring Framework
核心。它是所有 Spring
應(yīng)用的基石糖赔,所以我們應(yīng)該從上至下萍丐,由淺入深來進行學(xué)習和分析。