Spring Boot 3.x系列文章
- Spring Boot 2.7.8 中文参考指南(一)
- Spring Boot 2.7.8 中文参考指南(二)-Web
- Spring Boot 源码阅读初始化环境搭建
- Spring Boot 框架整体启动流程详解
- Spring Boot 系统初始化器详解
自定义系统初始化器
Spring Boot 有多种加载自定义初始化器的方法:
1、创建一个实现ApplicationContextInitializer接口的类,在spring.factories
中添加,如MyInitializer
2、创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers
添加,如MyInitializer2
3、创建一个实现ApplicationContextInitializer接口的类,在application.yml
或application.properties
中使用context.initializer.classes
添加,如MyInitializer3
4、创建一个实现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> {@Overridepublic 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> {@Overridepublic 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> {@Overridepublic 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 {@Overridepublic 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 应用程序初始化的时候会从META-INF/spring.factories加载ApplicationContextInitializer类实例
SpringFactoriesLoader
是Spring 框架中的类,用于从多个Jar文件的META-INF/spring.factories
中加载并实例化给定的类型,spring.factories
文件必须采用Properties
格式,其中key是接口或抽象类的完全限定名称,value是以逗号分隔的实现类名列表。例如:
example.MyService=example.MyServicesImpl1,example.MyService Impl2
其中example.MyService是接口的名称,MyServiceImpl1和MyServiceImpl2是两个实现。
获取实例分成了两部分,首先从多个Jar文件的META-INF/spring.factories
中加载key和value,返回一个SpringFactoriesLoader实例,然后调用SpringFactoriesLoader的load方法初始化指定key(key为接口或者抽象类的全限定名)对应的所有value(接口实现类),返回实例列表。
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");
// 获取资源对应的类加载器,若传入的类加载器为空,则使用SpringFactoriesLoader类的类加载器ClassLoader resourceClassLoader = (classLoader != null ? classLoader :SpringFactoriesLoader.class.getClassLoader());
// 从缓存中获取SpringFactoriesLoader,若不存在,则创建一个并缓存 Map<String, SpringFactoriesLoader>,key为ClassLoader,资源对应的类加载器Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
// 返回resourceLocation对应的SpringFactoriesLoader对象,若不存在,则创建一个并缓存,key为resourceLocation,资源路径return loaders.computeIfAbsent(resourceLocation, key ->new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}
computeIfAbsent
返回的是key关联的value值
最后一步value创建了一个SpringFactoriesLoader
实例,loadFactoriesResource
使用给定的资源类加载器从"META-INF/spring.factories"中加载
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
//实现列表,key=接口或抽象类全限定名 value=实现类全限定名Map<String, List<String>> result = new LinkedHashMap<>();try {//获取指定路径下所有的资源URLEnumeration<URL> urls = classLoader.getResources(resourceLocation);while (urls.hasMoreElements()) {UrlResource resource = new UrlResource(urls.nextElement());//从URL资源中读取配置Properties properties = PropertiesLoaderUtils.loadProperties(resource);properties.forEach((name, value) -> {//实现类逗号分割,转换为数组String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);//接口的实现类列表List<String> implementations = result.computeIfAbsent(((String) name).trim(),key -> new ArrayList<>(factoryImplementationNames.length));//去掉实现类两边空格,并插入实现类列表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);}//返回不可修改的mapreturn Collections.unmodifiableMap(result);
}
加载部分有很多的key,value 要分清楚。
spring.factories接口实现类的实例化
实例化通过调用SpringFactoriesLoader的load方法
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {return load(factoryType, argumentResolver, null);}
factoryType
指定要实例化的类型,这里为 org.springframework.context.ApplicationContextInitializer
argumentResolver
实例化需要的参数,这里为null
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,@Nullable FailureHandler failureHandler) {Assert.notNull(factoryType, "'factoryType' must not be null");//从factories 中获取指定接口类型的所有实现//factories就是加载步骤中返回的resultList<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;//循环,实例化for (String implementationName : implementationNames) {//通过构造函数实例化T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);if (factory != null) {result.add(factory);}}//根据order 排序AnnotationAwareOrderComparator.sort(result);return result;
}
最终返回排序后的ApplicationContextInitializer 实例,赋值SpringApplication 的 initializers 变量。
执行
执行会在SpringApplication类的prepareContext(准备上下文)中进行调用,如图所示:
//返回一个只读的有序,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.");// 调用ApplicationContextInitializer实例的initialize方法进行初始化操作initializer.initialize(context);}}
实例2加载原理
创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers
添加,如MyInitializer2
。
我们使用addInitializers 将ApplicationContextInitializer接口的实现加入到SpringApplication中。
public void addInitializers(ApplicationContextInitializer<?>... initializers) {this.initializers.addAll(Arrays.asList(initializers));}
initializers 就是 SpringApplication中的initializers变量,执行点同实例1,在准备上下文的时候执行,由于执行前会进行一次排序,所以他们两的顺序是正确的。
实例3加载原理
创建一个实现ApplicationContextInitializer接口的类,在application.yml
或application.properties
中使用context.initializer.classes
添加,如MyInitializer3
该处通过配置文件添加ApplicationContextInitializer实现类,并且通过DelegatingApplicationContextInitializer
初始化器进行加载和执行。
DelegatingApplicationContextInitializer 被定义在了spring-boot.jar 的 META-INF/spring.factories
中,并且由于他的order是0,所以会在我们自定义MyInitializer和MyInitializer2 前执行,它是另外一种独立的初始化器,专门用于将配置文件中的ApplicationContextInitializer实现类加载到Spring容器中。
执行在DelegatingApplicationContextInitializer类的applyInitializers方法中
private void applyInitializers(ConfigurableApplicationContext context,List<ApplicationContextInitializer<?>> initializers) {//排序initializers.sort(new AnnotationAwareOrderComparator());for (ApplicationContextInitializer initializer : initializers) {//调用initialize方法initializer.initialize(context);}
}
实例4加载原理
创建一个实现EnvironmentPostProcessor接口的类,在spring.factories
中添加,如MyEnvironmentPostProcessor
。
实例4是在所有的测试中最先打印日志的,是因为它是在prepareEnvironment
(准备环境)中执行,而前面3个实例都是在prepareContext(准备上下文)
中执行。
该实例中EventPublishingRunListener
会调用prepareEnvironment方法,EventPublishingRunListener
被定义在Spring Boot Jar包的META-INF/spring.factories
中,用于发布各种SpringApplicationEvent事件。
EventPublishingRunListener
类中
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {//广播环境准备完成事件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));// 获取执行事件的线程池Executor executor = getTaskExecutor();//获取指定事件类型的事件集合for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {//如果定义了执行线程池,则用线程池调用if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {//同步调用监听器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 {//此处执行事件监听器的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) {
//根据各个事件类型分别去处理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,然后执行其 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 是一个函数表达式EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
中的postProcessorsFactory是在EnvironmentPostProcessorApplicationListener
实例化的时候初始化,根据前面的文章我们知道EnvironmentPostProcessorApplicationListener
是一个监听器,会在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的时候调用,如果没有加载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类型的对象,参数为argumentResolverargumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);//加载EnvironmentPostProcessor类型的对象return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
}
最后循环调用postProcessor.postProcessEnvironment(environment, application);
完成执行。
总结
同样的,用一张图来总结本文整个流程:
作者其他文章:
Prometheus 系列文章
- Prometheus 的介绍和安装
- 直观感受PromQL及其数据类型
- PromQL之选择器和运算符
- PromQL之函数
- Prometheus 告警机制介绍及命令解读
- Prometheus 告警模块配置深度解析
- Prometheus 配置身份认证
- Prometheus 动态拉取监控服务
- Prometheus 监控云Mysql和自建Mysql
Grafana 系列文章,版本:OOS v9.3.1
- Grafana 的介绍和安装
- Grafana监控大屏配置参数介绍(一)
- Grafana监控大屏配置参数介绍(二)
- Grafana监控大屏可视化图表
- Grafana 查询数据和转换数据
- Grafana 告警模块介绍
- Grafana 告警接入飞书通知
Spring Boot Admin 系列
- Spring Boot Admin 参考指南
- SpringBoot Admin服务离线、不显示健康信息的问题
- Spring Boot Admin2 @EnableAdminServer的加载
- Spring Boot Admin2 AdminServerAutoConfiguration详解
- Spring Boot Admin2 实例状态监控详解
- Spring Boot Admin2 自定义JVM监控通知
- Spring Boot Admin2 自定义异常监控
- Spring Boot Admin 监控指标接入Grafana可视化