简介
其实spring启动步骤中最早可以进行扩展的是实现ApplicationContextInitializer
接口。来看看这个接口的注释。
package org.springframework.context;
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the {@link org.springframework.core.annotation.Order @Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* @author Chris Beams
* @since 3.1
* @param <C> the application context type
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
*/
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
简要的说明一下,有这么几点:
- 实现这个接口之后,它的initialize方法会在容器
ConfigurableApplicationContext
刷新之前触发。 - 它通常用于在容器初始化之前进行一些程序上的操作,比如说注册一些环境变量或者读取一些配置文件。
- 它可以使用@Order指定优先级
实现方式
它有三种实现方式:
- 通过SPI机制实现,在
resources/META-INF/spring.factories
中定义如下内容:
org.springframework.context.ApplicationContextInitializer=com.alone.spring.aop.demo.config.ContextInitializerTest
/**
* spring扩展点 ApplicationContextInitializer
*/
@Slf4j
public class ContextInitializerTest implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
log.info("ContextInitializerTest 开始加载");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> initMap = new HashMap<>();
initMap.put("20231116", "This is init");
MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);
environment.getPropertySources().addLast(propertySource);
log.info("ContextInitializerTest 加载结束");
}
}
- 在
application.yml
中定义如下内容:
context:
initializer:
classes: com.alone.spring.aop.demo.config.YmlApplicationContextInitializer
@Slf4j
public class YmlApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
log.info("这是yml的ApplicationContextInitializer");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> initMap = new HashMap<>();
initMap.put("20231116", "YmlApplicationContextInitializer");
MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);
environment.getPropertySources().addLast(propertySource);
log.info("YmlApplicationContextInitializer 加载结束");
}
}
- 在启动类中进行注册:
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
springApplication.addInitializers(new MainFlagApplicationContextInitializer());
springApplication.run();
}
@Component
@Slf4j
public class MainFlagApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
log.info("这是main的ApplicationContextInitializer");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> initMap = new HashMap<>();
initMap.put("20231116", "MainFlagApplicationContextInitializer");
MapPropertySource propertySource = new MapPropertySource("ContextInitializerTest", initMap);
environment.getPropertySources().addLast(propertySource);
log.info("MainFlagApplicationContextInitializer 加载结束");
}
}
三者的加载顺序是:
application.yml
>spring.factories
>启动类
源码分析
从启动类的new SpringApplication(SpringbootApplication.class)
开始分析:
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 = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
看到上面第8行(源码266行)中出现了ApplicationContextInitializer.class
猜想它肯定是在读取相关的配置,跟进去发现出现了下面这行。
这里是读取了spring.factories
中的内容,但看它的结果发现不止我们自定义的类一个,说明springboot内置了一些ApplicationContextInitializer
,后续我们再看它们具体的作用,这里先截图列出按下不表。
然后沿如下的调用栈可以找到initializer.initialize(context)
;这一行调用ApplicationContextInitializer
的语句。
● springApplication.run()
● run:306, SpringApplication (org.springframework.boot)
● prepareContext:383, SpringApplication (org.springframework.boot)
● applyInitializers:614, SpringApplication (org.springframework.boot)
框起来的方法会对所有的initializer进行排序,排序后的结果见左边。
在执行到DelegatingApplicationContextInitializer
时会去读取环境中的context.initializer.classes
,也就是application.yml
中配置的内容执行。所以会先执行yml配置的initializer.
以上总结一下是这样的:
大致调用的流程图是:
系统内置初始化类
最后我们来看看上面提到的系统内置的初始化类都有些什么作用。
SharedMetadataReaderFactoryContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);
applicationContext.addBeanFactoryPostProcessor(postProcessor);
}
初始化了一个CachingMetadataReaderFactoryPostProcessor
至容器中
DelegatingApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
applyInitializerClasses(context, initializerClasses);
}
}
执行context.initializer.classes
配置的initializer。
ContextIdApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ContextId contextId = getContextId(applicationContext);
applicationContext.setId(contextId.getId());
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
}
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
ApplicationContext parent = applicationContext.getParent();
if (parent != null && parent.containsBean(ContextId.class.getName())) {
return parent.getBean(ContextId.class).createChildId();
}
return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}
private String getApplicationId(ConfigurableEnvironment environment) {
String name = environment.getProperty("spring.application.name");
return StringUtils.hasText(name) ? name : "application";
}
设置容器的id,值取自spring.application.name
配置,默认是application
ConditionEvaluationReportLoggingListener
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
if (applicationContext instanceof GenericApplicationContext) {
// Get the report early in case the context fails to load
this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
}
}
注册了一个ConditionEvaluationReportListener
RestartScopeInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerScope("restart", new RestartScope());
}
自动重启相关。
ConfigurationWarningsApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
初始化一个ConfigurationWarningsPostProcessor用于记录公共的容器配置错误信息。
RSocketPortInfoApplicationContextInitializer
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(new Listener(applicationContext));
}
增加了一个监听器用于监听RSockerServer的端口是否正常。
ServerPortInfoApplicationContextInitializer
/**
* {@link ApplicationContextInitializer} that sets {@link Environment} properties for the
* ports that {@link WebServer} servers are actually listening on. The property
* {@literal "local.server.port"} can be injected directly into tests using
* {@link Value @Value} or obtained via the {@link Environment}.
* <p>
* If the {@link WebServerInitializedEvent} has a
* {@link WebServerApplicationContext#getServerNamespace() server namespace} , it will be
* used to construct the property name. For example, the "management" actuator context
* will have the property name {@literal "local.management.port"}.
* <p>
* Properties are automatically propagated up to any parent context.
*
* @author Dave Syer
* @author Phillip Webb
* @since 2.0.0
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(this);
}
向容器中增加一个监听器用于检测WebServer的端口是否正常监听。
参考资料
- SpringBoot系统初始化器使用及源码解析(ApplicationContextInitializer)
- 跟我一起阅读SpringBoot源码(九)——初始化执行器
- Springboot扩展点之ApplicationContextInitializer