學(xué)習(xí)筆記是學(xué)習(xí)了 小馬哥在慕課網(wǎng)的 《Spring Boot 2.0深度實(shí)踐之核心技術(shù)篇》根據(jù)自己的需要和理解做的筆記皱坛。
應(yīng)用上下文初始器(ApplicationContextInitializer)
利用Spring工廠機(jī)制,實(shí)現(xiàn)類org.springframework.core.io.support.SpringFactoriesLoader
實(shí)例化 ApplicationContextInitializer
實(shí)現(xiàn)類着裹,并排序?qū)ο蠹稀?/p>
自己追了一下源碼,發(fā)現(xiàn)Spring-boot加載應(yīng)用文上下初始器的順序是
org.springframework.boot.SpringApplication#SpringApplication()
->org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
->org.springframework.boot.SpringApplicatior#run()
具體代碼實(shí)現(xiàn)org.springframework.boot.SpringApplication#SpringApplication()
1.spring-boot使用setInitializers
方法加載所有實(shí)現(xiàn)ApplicationContextInitializer.class
的類,即全部上下文實(shí)現(xiàn)類助被。
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
//加載上下文初始器 ApplicationContextInitializer.class 的實(shí)現(xiàn)類
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//加載監(jiān)聽初始器 ApplicationListener.class 的實(shí)現(xiàn)類
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
2.我們來看一下加載上下文實(shí)體的方法
org.springframework.boot.SpringApplication#getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
//在這里調(diào)用了 SpringFactoriesLoader 中的loadFactoryNames方法蠢涝,
//來插入想要實(shí)現(xiàn)的上下文
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
在這里注意一點(diǎn)就是spring-boot 使用了 Thread.currentThread().getContextClassLoader()
來獲取了當(dāng)前類加載器鲸阻,這里就要介紹一下SPI機(jī)制(我也是在看這段代碼中,無法理解這段跋涣,在網(wǎng)上查了一些資料,簡(jiǎn)單的理解了一下這里為什么用這個(gè)方法鸟悴。)
SPI機(jī)制(Service Provider Interface)其實(shí)源自服務(wù)提供者框架(Service Provider Framework陈辱,參考【EffectiveJava】page6),是一種將服務(wù)接口與服務(wù)實(shí)現(xiàn)分離以達(dá)到解耦细诸、大大提升了程序可擴(kuò)展性的機(jī)制沛贪。引入服務(wù)提供者就是引入了spi接口的實(shí)現(xiàn)者,通過本地的注冊(cè)發(fā)現(xiàn)獲取到具體的實(shí)現(xiàn)類震贵,輕松可插拔利赋。
這里的ClassLoader
是spring-boot的類加載器,在這突然又想到了JVM的雙親委派模型,感興趣的同學(xué)可以了解一下猩系。那么這個(gè)類加載器的SPI機(jī)制實(shí)現(xiàn)類都有哪些媚送? 就是配置資源META-INF/spring.factories
中的內(nèi)容。
3.我們來看一下引用SpringFactoriesLoader#SpringFactoriesLoader
的方法是做什么的寇甸。
/**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* {@code null} to use the default
* @see #loadFactories
* @throws IllegalArgumentException if an error occurs while loading factory names
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
在源碼注釋中可以清晰的看到該方法的作用塘偎,首先要了解到FACTORIES_RESOURCE_LOCATION
全局變量的定義
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
META-INF/spring.factories存放的就是所有spring-boot實(shí)現(xiàn)的各種繼承規(guī)約接口的實(shí)現(xiàn)類的類路徑。
了解了這個(gè)全局變量拿霉,那么這段注釋我們就可以明白了吟秩。
/**Load the fully qualified class names of factory implementations of the given type from {@value #FACTORIES_RESOURCE_LOCATION}*/
從FACTORIES_RESOURCE_LOCATION
中獲取所有繼承已知工廠類別的類全限定名。
4.既然都到這了绽淘,我們就不能不看一下SpringFactoriesLoader#loadSpringFactories(classLoader)
這個(gè)方法涵防。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
/**
* 獲取所有實(shí)現(xiàn)工廠接口的類的全限定名
*/
Enumeration<URL> urls = (classLoader != null ?
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()) {
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);
}
}
通過 classLoader.getResources
和ClassLoader.getSystemResources
來獲取META-INF/spring.factories
下的內(nèi)容。比如:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
然后通過解析Properties內(nèi)容來獲取全限定名收恢。
最后將想獲取的全限定名返回給
org.springframework.boot.SpringApplication#getSpringFactoriesInstances()
方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//使用反射機(jī)制生成實(shí)例對(duì)象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//進(jìn)行實(shí)例集合排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通過#createSpringFactoriesInstances
方法生成實(shí)例以及調(diào)用AnnotationAwareOrderComparator.sort()
進(jìn)行排序武学,而排序需要實(shí)現(xiàn)org.springframework.core.Ordered
接口或者標(biāo)注org.springframework.core.Ordered
注解,排序不是強(qiáng)制性的伦意。就像META-INF/spring.factories
中應(yīng)用上下文初始器的實(shí)現(xiàn)類 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
和
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
都沒有實(shí)現(xiàn)排序的接口或者注解火窒。
說了這么多,接下來讓我們自己實(shí)現(xiàn)一個(gè)上下文初始器吧驮肉。