Spring Boot 3.x系列文章
- Spring Boot 2.7.8 中文參考指南(一)
- Spring Boot 2.7.8 中文參考指南(二)-Web
- Spring Boot 源碼閱讀初始化環(huán)境搭建
- Spring Boot 框架整體啟動流程詳解
- Spring Boot 系統(tǒng)初始化器詳解
自定義系統(tǒng)初始化器
Spring Boot 有多種加載自定義初始化器的方法:
1、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在spring.factories
中添加,如MyInitializer
2、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在SpringApplication 中使用addInitializers
添加,如MyInitializer2
3、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在application.yml
或application.properties
中使用context.initializer.classes
添加,如MyInitializer3
4、創(chuàng)建一個實現(xiàn)EnvironmentPostProcessor接口的類,在spring.factories
中添加,如MyEnvironmentPostProcessor
代碼如下所示:
MyInitializer.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Order(2)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>();
map.put("key", "value");
MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
configurableEnvironment.getPropertySources().addLast(mapPropertySource);
log.info("My Initializer run");
}
}
MyInitializer2.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Order(1)
public class MyInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>();
map.put("key2", "value2");
MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
configurableEnvironment.getPropertySources().addLast(mapPropertySource);
log.info("My Initializer2 run");
}
}
MyInitializer3.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Order(10)
public class MyInitializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>();
map.put("key3", "value3");
MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
configurableEnvironment.getPropertySources().addLast(mapPropertySource);
log.info("My Initializer3 run");
}
}
MyEnvironmentPostProcessor.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Order(5)
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> map = new HashMap<>();
map.put("key", "value");
MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
environment.getPropertySources().addLast(mapPropertySource);
//為什么不打印日志
// log.info("My EnvironmentPostProcessor run");
System.out.println("My EnvironmentPostProcessor run");
}
}
啟動后截圖:
疑問?
- 在MyEnvironmentPostProcessor的示例中,用
log.info("My EnvironmentPostProcessor run");
不會打印日志。- MyInitializer3的輸出怎么會在MyInitializer2之前。
加載原理
實例1加載原理
在之前的文章中《Spring Boot 框架整體啟動流程詳解》有介紹到Spring Boot 應(yīng)用程序初始化的時候會從META-INF/spring.factories加載ApplicationContextInitializer類實例
SpringFactoriesLoader
是Spring 框架中的類,用于從多個Jar文件的META-INF/spring.factories
中加載并實例化給定的類型,spring.factories
文件必須采用Properties
格式,其中key是接口或抽象類的完全限定名稱,value是以逗號分隔的實現(xiàn)類名列表。例如:
example.MyService=example.MyServicesImpl1,example.MyService Impl2
其中example.MyService是接口的名稱,MyServiceImpl1和MyServiceImpl2是兩個實現(xiàn)。
獲取實例分成了兩部分,首先從多個Jar文件的META-INF/spring.factories
中加載key和value,返回一個SpringFactoriesLoader實例,然后調(diào)用SpringFactoriesLoader的load方法初始化指定key(key為接口或者抽象類的全限定名)對應(yīng)的所有value(接口實現(xiàn)類),返回實例列表。
spring.factories的加載
FACTORIES_RESOURCE_LOCATION
指定了加載的路徑
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
// 判斷資源路徑是否為空,若為空則拋出異常
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
// 獲取資源對應(yīng)的類加載器,若傳入的類加載器為空,則使用SpringFactoriesLoader類的類加載器
ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
SpringFactoriesLoader.class.getClassLoader());
// 從緩存中獲取SpringFactoriesLoader,若不存在,則創(chuàng)建一個并緩存 Map<String, SpringFactoriesLoader>,key為ClassLoader,資源對應(yīng)的類加載器
Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(
resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
// 返回resourceLocation對應(yīng)的SpringFactoriesLoader對象,若不存在,則創(chuàng)建一個并緩存,key為resourceLocation,資源路徑
return loaders.computeIfAbsent(resourceLocation, key ->
new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}
computeIfAbsent
返回的是key關(guān)聯(lián)的value值
最后一步value創(chuàng)建了一個SpringFactoriesLoader
實例,loadFactoriesResource
使用給定的資源類加載器從"META-INF/spring.factories"中加載
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
//實現(xiàn)列表,key=接口或抽象類全限定名 value=實現(xiàn)類全限定名
Map<String, List<String>> result = new LinkedHashMap<>();
try {
//獲取指定路徑下所有的資源URL
Enumeration<URL> urls = classLoader.getResources(resourceLocation);
while (urls.hasMoreElements()) {
UrlResource resource = new UrlResource(urls.nextElement());
//從URL資源中讀取配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
properties.forEach((name, value) -> {
//實現(xiàn)類逗號分割,轉(zhuǎn)換為數(shù)組
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);
//接口的實現(xiàn)類列表
List<String> implementations = result.computeIfAbsent(((String) name).trim(),
key -> new ArrayList<>(factoryImplementationNames.length));
//去掉實現(xiàn)類兩邊空格,并插入實現(xiàn)類列表
Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);
});
}
//去重
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
}
//返回不可修改的map
return Collections.unmodifiableMap(result);
}
加載部分有很多的key,value 要分清楚。
spring.factories接口實現(xiàn)類的實例化
實例化通過調(diào)用SpringFactoriesLoader的load方法
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {
return load(factoryType, argumentResolver, null);
}
factoryType
指定要實例化的類型,這里為 org.springframework.context.ApplicationContextInitializerargumentResolver
實例化需要的參數(shù),這里為null
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
@Nullable FailureHandler failureHandler) {
Assert.notNull(factoryType, "'factoryType' must not be null");
//從factories 中獲取指定接口類型的所有實現(xiàn)
//factories就是加載步驟中返回的result
List<String> implementationNames = loadFactoryNames(factoryType);
logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
List<T> result = new ArrayList<>(implementationNames.size());
//定義失敗處理器
FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;
//循環(huán),實例化
for (String implementationName : implementationNames) {
//通過構(gòu)造函數(shù)實例化
T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
if (factory != null) {
result.add(factory);
}
}
//根據(jù)order 排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
最終返回排序后的ApplicationContextInitializer 實例,賦值SpringApplication 的 initializers 變量。
執(zhí)行
執(zhí)行會在SpringApplication類的prepareContext(準備上下文)中進行調(diào)用,如圖所示:
//返回一個只讀的有序,LinkedHashSet 類型
public Set<ApplicationContextInitializer<?>> getInitializers() {
return asUnmodifiableOrderedSet(this.initializers);
}
protected void applyInitializers(ConfigurableApplicationContext context) {
//獲取所有的ApplicationContextInitializer實例
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
// 判斷ApplicationContextInitializer實例泛型是否與context對象類型一致
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 調(diào)用ApplicationContextInitializer實例的initialize方法進行初始化操作
initializer.initialize(context);
}
}
實例2加載原理
創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在SpringApplication 中使用addInitializers
添加,如MyInitializer2
。
我們使用addInitializers 將ApplicationContextInitializer接口的實現(xiàn)加入到SpringApplication中。
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
this.initializers.addAll(Arrays.asList(initializers));
}
initializers 就是 SpringApplication中的initializers變量,執(zhí)行點同實例1,在準備上下文的時候執(zhí)行,由于執(zhí)行前會進行一次排序,所以他們兩的順序是正確的。
實例3加載原理
創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在application.yml
或application.properties
中使用context.initializer.classes
添加,如MyInitializer3
該處通過配置文件添加ApplicationContextInitializer實現(xiàn)類,并且通過DelegatingApplicationContextInitializer
初始化器進行加載和執(zhí)行。
DelegatingApplicationContextInitializer 被定義在了spring-boot.jar 的 META-INF/spring.factories
中,并且由于他的order是0,所以會在我們自定義MyInitializer和MyInitializer2 前執(zhí)行,它是另外一種獨立的初始化器,專門用于將配置文件中的ApplicationContextInitializer實現(xiàn)類加載到Spring容器中。
執(zhí)行在DelegatingApplicationContextInitializer類的applyInitializers方法中
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
//排序
initializers.sort(new AnnotationAwareOrderComparator());
for (ApplicationContextInitializer initializer : initializers) {
//調(diào)用initialize方法
initializer.initialize(context);
}
}
實例4加載原理
創(chuàng)建一個實現(xiàn)EnvironmentPostProcessor接口的類,在spring.factories
中添加,如MyEnvironmentPostProcessor
。
實例4是在所有的測試中最先打印日志的,是因為它是在prepareEnvironment
(準備環(huán)境)中執(zhí)行,而前面3個實例都是在prepareContext(準備上下文)
中執(zhí)行。
該實例中EventPublishingRunListener
會調(diào)用prepareEnvironment方法,EventPublishingRunListener
被定義在Spring Boot Jar包的META-INF/spring.factories
中,用于發(fā)布各種SpringApplicationEvent事件。
EventPublishingRunListener
類中
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
//廣播環(huán)境準備完成事件
multicastInitialEvent(
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
private void multicastInitialEvent(ApplicationEvent event) {
//刷新SimpleApplicationEventMulticaster中的事件列表
refreshApplicationListeners();
//廣播事件
this.initialMulticaster.multicastEvent(event);
}
private void refreshApplicationListeners() {
this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
}
SimpleApplicationEventMulticaster
類中
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, null);
}
@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
// 獲取執(zhí)行事件的線程池
Executor executor = getTaskExecutor();
//獲取指定事件類型的事件集合
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//如果定義了執(zhí)行線程池,則用線程池調(diào)用
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//同步調(diào)用監(jiān)聽器
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//獲取失敗處理器
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//此處執(zhí)行事件監(jiān)聽器的onApplicationEvent方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent payloadEvent &&
matchesClassCastMessage(msg, payloadEvent.getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
在listener.onApplicationEvent(event);
處,在本例中為EnvironmentPostProcessorApplicationListener
EnvironmentPostProcessorApplicationListener
類中:
public void onApplicationEvent(ApplicationEvent event) {
//根據(jù)各個事件類型分別去處理
if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent();
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
// 獲取所有的 EnvironmentPostProcessor,然后執(zhí)行其 postProcessEnvironment 方法
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
// 獲取所有的 EnvironmentPostProcessor
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
ConfigurableBootstrapContext bootstrapContext) {
ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
//postProcessorsFactory 是一個函數(shù)表達式
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
中的postProcessorsFactory是在EnvironmentPostProcessorApplicationListener
實例化的時候初始化,根據(jù)前面的文章我們知道EnvironmentPostProcessorApplicationListener
是一個監(jiān)聽器,會在SpringBoot初始化的時候初始化。
public EnvironmentPostProcessorApplicationListener() {
this(EnvironmentPostProcessorsFactory::fromSpringFactories);
}
private EnvironmentPostProcessorApplicationListener(
Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory) {
this.postProcessorsFactory = postProcessorsFactory;
this.deferredLogs = new DeferredLogs();
}
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
return new SpringFactoriesEnvironmentPostProcessorsFactory(
SpringFactoriesLoader.forDefaultResourceLocation(classLoader));
}
EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader)
會在EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
apply的時候調(diào)用,如果沒有加載META-INF/spring.factories
會再這里再次加載。
EnvironmentPostProcessorsFactory
的主要作用是實例化EnvironmentPostProcessor
,SpringFactoriesEnvironmentPostProcessorsFactory
是其子類。
SpringFactoriesEnvironmentPostProcessorsFactory
類中:
public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
ConfigurableBootstrapContext bootstrapContext) {
ArgumentResolver argumentResolver = ArgumentResolver.of(DeferredLogFactory.class, logFactory);
//向argumentResolver對象中添加ConfigurableBootstrapContext.class和bootstrapContext,獲取更新后的argumentResolver對象
argumentResolver = argumentResolver.and(ConfigurableBootstrapContext.class, bootstrapContext);
// // 向argumentResolver對象中添加BootstrapRegistry.class和bootstrapContext,獲取更新后的argumentResolver對象
argumentResolver = argumentResolver.and(BootstrapContext.class, bootstrapContext);
通過this.loader.load方法加載EnvironmentPostProcessor類型的對象,參數(shù)為argumentResolver
argumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);
//加載EnvironmentPostProcessor類型的對象
return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
}
最后循環(huán)調(diào)用postProcessor.postProcessEnvironment(environment, application);
完成執(zhí)行。
總結(jié)
同樣的,用一張圖來總結(jié)本文整個流程:
作者其他文章:
Prometheus 系列文章
- Prometheus 的介紹和安裝
- 直觀感受PromQL及其數(shù)據(jù)類型
- PromQL之選擇器和運算符
- PromQL之函數(shù)
- Prometheus 告警機制介紹及命令解讀
- Prometheus 告警模塊配置深度解析
- Prometheus 配置身份認證
- Prometheus 動態(tài)拉取監(jiān)控服務(wù)
- Prometheus 監(jiān)控云Mysql和自建Mysql
Grafana 系列文章,版本:OOS v9.3.1文章來源:http://www.zghlxwxcb.cn/news/detail-509230.html
- Grafana 的介紹和安裝
- Grafana監(jiān)控大屏配置參數(shù)介紹(一)
- Grafana監(jiān)控大屏配置參數(shù)介紹(二)
- Grafana監(jiān)控大屏可視化圖表
- Grafana 查詢數(shù)據(jù)和轉(zhuǎn)換數(shù)據(jù)
- Grafana 告警模塊介紹
- Grafana 告警接入飛書通知
Spring Boot Admin 系列文章來源地址http://www.zghlxwxcb.cn/news/detail-509230.html
- Spring Boot Admin 參考指南
- SpringBoot Admin服務(wù)離線、不顯示健康信息的問題
- Spring Boot Admin2 @EnableAdminServer的加載
- Spring Boot Admin2 AdminServerAutoConfiguration詳解
- Spring Boot Admin2 實例狀態(tài)監(jiān)控詳解
- Spring Boot Admin2 自定義JVM監(jiān)控通知
- Spring Boot Admin2 自定義異常監(jiān)控
- Spring Boot Admin 監(jiān)控指標接入Grafana可視化
到了這里,關(guān)于Spring Boot 系統(tǒng)初始化器詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!