几个重要的事件回调机制
ApplicationContextInitializer
- ApplicationContextInitializer来源于Spring框架
- 主要作用就是在ConfigurableApplicationContext类型或者子类型的ApplicationContext做refresh之前
- 允许对ConfigurableApplicationContext的实例做进一步的设置和处理
- ApplicationContextInitializer接口:
- 是在Spring容器刷新之前执行的一个回调函数
- 是在ConfigurableApplicationContext的refresh() 方法之前,即在Spring框架内部执行ConfigurableApplicationContext的refresh() 方法或者SpringBoot的run() 方法之前调用
- 作用是初始化Spring的ConfigurableApplicationContext的回调接口
- 通常用于需要对应用上下文进行初始化的web应用程序中: 比如根据上下文环境注册属性或者激活概要文件
使用分析
- ApplicationContextInitializer接口的典型应用场景:
- 对web应用程序的应用上下文进行初始化
- 比如:
- 注册属性源property sources
- 针对上下文的环境信息environment激活相应的profile
- 在一个SpringBoot的应用程序中:
- classpath上有很多jar包,有些jar包需要在ConfigurableApplicationContext的refresh() 方法调用之前对应用上下文做一些初始化动作
- 因此会提供自己的ApplicationContextInitializer实现类,然后配置在自己的META-INF/spring.factories属性文件中
- 这样相应的ApplicationContextInitializer实现类就会被SpringApplication的initialize() 方法发现
- SpringApplication的initialize() 方法,在SpringApplication的构造函数内执行,从而确保在SpringApplication的run() 方法之前完成
- 然后在应用上下文创建之后,应用上下文刷新之前的准备阶段被调用
SpringBoot内置的ApplicationContextInitializer
- 使用SpringBoot web应用默认使用的ApplicationContextInitializer的实现:
- DelegatingApplicationContextInitializer:
- 使用环境属性context.initializer.classes指定的初始化容器initializer进行初始化工作,如果没有指定则不进行任何操作
- 使得可以在application.properties中可以自定义实现类配置
- ContextIdApplicationContextInitializer:
- 参照环境属性,设置Spring应用上下文的ID
- ID值的设置会参照环境属性:
- spring.application.name
- vcap.application.name
- spring.config.name
- spring.application.index
- vcap.application.instance_index
- 如果这些属性都没有 ,ID使用application
- ConfigurationWarningApplicationContextInitializer:
- ServerPortInfoApplicationContextInitializer:
- 将内置servlet容器实际使用的监听端口写入到environment环境属性中
- 这样属性local.server.port就可以直接通过 @Value注入到测试中或者通过环境属性environment获取
- SharedMetadataReaderFactoryContextInitializer:
- 创建一个SpringBoot和ConfigurationClassPostProcessor共用的CachingMetadataReaderFactory对象
- 实现类为ConcurrentReferrenceCachingMetadataReaderFactory
- ConditionEvaluationReportLoggingListener:
- 将ConditionEvaluationReport写入日志
- ApplicationContextInitializer是Spring中允许在上下文刷新之前做自定义操作,如果需要对Spring的上下文进行深度整合,可以借助ApplicationContextInitializer进行很好的实现
- spring-test包里有一个注解org.springframework.test.context.ContextConfiguration中有一个属性可以指定ApplicationContextInitializer辅助集成测试时自定义对上下文进行预处理
扩展实现方式
编程方式
- 先定义ApplicationContextInitializer:
// @Order(66) - @Order的值越小就越早执行. 标注在类上, 不是方法上
@Order(66)
public class customerApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 输出容器中有多少个bean
System.out.println("Bean的数量为: " + applicationContext.getBeanDefinitionCount());
/*
* 输出容器中所有bean的beanName
*/
System.out.println(applicationContext.getBeanDefinitionCount + "个Bean的名称:");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames) {
System.out.println(beanName);
}
}
}
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServer {
public static void mian(String[] args) {
SpringApplication springApplication = new SpringApplication(ConfigServer.class);
// 添加自定义的ApplicationContextInitializer实现类的实例用来注册ApplicationContextInitializer
springApplication.addInitializers(new customerApplicationContextInitializer());
ConfigurableApplicationContext applicationContext = springApplication.run(args);
applicationContext.close();
}
}
添加配置方式
spring.factories方式
- ==SpringApplicationRunListener==
- ApplicationContextInitializer,SpringApplicationRunListener需要配置在META-INF/spring.factories中
- ==ApplicationRunner==
- ==CommandLineRunner==
- ApplicationRunner,CommandLineRunner需要放在IOC容器中
启动流程
- 创建SpringApplication对象
- 调用initialize(sources)方法创造对象
- 保存主配置类
- 判断当前是否为一个web应用
- 从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然后保存起来
- 从从类路径下找到META-INF/spring.factories配置的所有ApplicatListener
- 从多个配置类中找到有main方法的主配置类
- 运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 停止监听
ConfigurableApplicationContext context = null; // 声明一个IOC容器
Collection exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
- 获取SpringApplicationRunListeners,从类路径下META-INF/spring.factories
- 回调所有的获取SpringApplicationRunListener.starting()方法
- 封装命令行参数
- 准备环境prepareEnvironment,创建环境完成后回调SpringApplicationRunListeners.environmentPrepared():表示环境准备完成
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
- 创建ApplicationContext:决定创建web的IOC还是普通的IOC
protected ConfigurableApplicationContext createApplicationContext() {
Class contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
- 准备上下文环境prepareContext:将environment保存到IOC中,并且调用applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法.然后回调SpringApplicationRunListener的contextPrepared方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
Set