文章目录
Spring Boot 启动核心
众所周知的SpringApplication.run(xxx.class, args);
SpringApplication启动
启动入口
@SpringBootApplication
public class xxxApplication {
public static void main(String[] args) {
SpringApplication.run(xxxApplication.class, args);
}
}
SpringApplication.run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
SpringApplication构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 设置资源加载器
this.resourceLoader = resourceLoader;
// 设置主要资源类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断当前应用的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置ApplicationContext的初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置Application监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断并设置主类
this.mainApplicationClass = deduceMainApplicationClass();
}
deduceFromClasspathpuan推断当前应用的类型
static WebApplicationType deduceFromClasspath() {
// 类路径中是否包含DispatcherHandler,且不包含DispatcherServlet,也不包含ServletContainer,那么是reactive应用
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果Servlet或者ConfigurableWebApplicationContext不存在,那么就是非web应用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 否则都是Servlet的web应用
return WebApplicationType.SERVLET;
}
getSpringFactoriesInstances(ApplicationContextInitializer.class)
从META-INF/spring.factories
配置文件中找到所有ApplicationContextInitializer接口对应的实现类配置,然后通过反射机制构造出对应的实例对象。
getSpringFactoriesInstances(ApplicationListener.class)
这个方法也是一样的做法,将会获取ApplicationListener接口的所有配置实例对象
deduceMainApplicationClass根据堆栈来推断当前main方法所在的主类
private Class<?> deduceMainApplicationClass() {
try {
// 获取堆栈链路
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 遍历每一个栈帧信息
for (StackTraceElement stackTraceElement : stackTrace) {
// 如果该栈帧对应的方法名等于main
if ("main".equals(stackTraceElement.getMethodName())) {
// 获取该类的class对象
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication.run
// 运行Spring应用程序,创建并刷新ApplicationContext
// @param args应用程序参数(通常从Java main方法传递)
// 返回正在运行的{@link ApplicationContext}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
组件
ApplicationListener
ApplicationListener 其实是老面孔,属于 Spring 框架对 Java 中实现的监听者模式的一种框架实现,这里唯一值得着重强调的是,对于初次接触 SpringBoot,但对 Spring 框架本身又没有过多接触的开发者来说,可能会将这个名字与 SpringApplicationRunListener 混淆。
关于 ApplicationListener 我们就不做过多介绍了,如果感兴趣,请参考 Spring 框架相关的资料和书籍。
如果我们要为 SpringBoot 应用添加自定义的 ApplicationListener,有两种方式:
- 通过 SpringApplication.addListeners(…)或者 SpringApplication.setListeners(…)方法添加一个或者多个自定义的 ApplicationListener。
- 借助 SpringFactoriesLoader 机制,在 META-INF/spring.factories 文件中添加配置(以下代码是为 SpringBoot 默认注册的 ApplicationListener 配置)。
org.springframework.context.ApplicationListener=
\org.springframework.boot.builder.ParentContextCloserApplicationListener,
\org.springframework.boot.cloudfoundry.VcapApplicationListener,
\org.springframework.boot.context.FileEncodingApplicationListener,
\org.springframework.boot.context.config.AnsiOutputApplicationListener,
\org.springframework.boot.context.config.ConfigFileApplicationListener,
\org.springframework.boot.context.config.DelegatingApplicationListener,
\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicat-ionListener,
\org.springframework.boot.logging.ClasspathLoggingApplicationListener,
\org.springframework.boot.logging.LoggingApplicationListener
关于 ApplicationListener,我们就说这些。
ApplicationContextInitializer
ApplicationContextInitializer 也是 Spring 框架原有的概念,这个类的主要目的就是在 ConfigurableApplicationContext 类型(或者子类型)的 ApplicationContext 做 refresh 之前,允许我们对 ConfigurableApplicationContext 的实例做进一步的设置或者处理。
实现一个 ApplicationContextInitializer 很简单,因为它只有一个方法需要实现:
public class DemoApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { // do whatever you want with applicationContext, // e.g. applicationContext.registerShutdownHook(); }}
不过,一般情况下我们基本不会需要自定义一个 ApplicationContext-Initializer,即使 SpringBoot 框架默认也只是注册了三个实现:
org.springframework.context.ApplicationContextInitializer=
\org.springframework.boot.context.ConfigurationWarningsApplication-ContextInitializer,
\org.springframework.boot.context.ContextIdApplicationContextInitia-lizer,
\org.springframework.boot.context.config.DelegatingApplicationContex-tInitializer
如果我们真的需要自定义一个 ApplicationContextInitializer,那么只要像上面这样,通过 SpringFactoriesLoader 机制进行配置,或者通过 SpringApplication.addInitializers(…)设置即可。
1. SpringApplicationRunListener
- 从
META-INF/spring.factories
获取所有SpringApplicationRunListener.class
的配置 - 通过反射获取
SpringApplicationRunListener
的实现类对象 - 把所有的实现类包装成
SpringApplicationRunListeners
核心代码:
// 把所有的实现类包装成`SpringApplicationRunListeners`
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
// 从`META-INF/spring.factories`获取所有`SpringApplicationRunListener.class`的配置
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。
源码如下:
public interface SpringApplicationRunListener {
// SpringApplication.run之后里面调用
default void starting() {}
// environment创建之后,ApplicationContext之前
default void environmentPrepared(ConfigurableEnvironment environment) {}
// ApplicationContext创建之后,sources 之前
default void contextPrepared(ConfigurableApplicationContext context) {}
// ApplicationContext加载之后,ApplicationContext刷新之前
default void contextLoaded(ConfigurableApplicationContext context) {}
// ApplicationContext刷新之后,CommandLineRunners、ApplicationRunner调用之前
default void started(ConfigurableApplicationContext context) {}
// CommandLineRunners、ApplicationRunner调用之后,兜底的了
default void running(ConfigurableApplicationContext context) {}
//在运行应用程序时发生故障时调用
default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}
看下在代码中调用位置:
EventPublishingRunListener(唯一默认实现)
默认只有一个实现类 EventPublishingRunListener类
EventPublishingRunListener类 实现了SpringApplicationRunListener,它具有广播事件的功能。
事件一览:
- starting - ApplicationStartingEvent
- environmentPrepared - ApplicationEnvironmentPreparedEvent
- contextPrepared - ApplicationContextInitializedEvent
- contextLoaded - ApplicationPreparedEvent
- started - ApplicationStartedEvent
- running - ApplicationReadyEvent
- failed - ApplicationFailedEvent
如何扩展
直接参照EventPublishingRunListener
实现
-
实现接口
SpringApplicationRunListener
-
META-INF/spring.factories
下配置org.springframework.boot.SpringApplicationRunListener=\ com.xxx.xxx.xxx
基本上不会去实现这个扩展。要用也是监听
EventPublishingRunListener
类发出的事件ApplicationEvent
。
总结
- SpringApplicationRunListener就是一个ApplicationListener的代理。
- SpringApplicationRunListeners是SpringApplicationRunListener的封装,SpringApplicationRunListeners中包含多个SpringApplicationRunListener,是为了批量执行的封装
- 使用了Spring广播器SimpleApplicationEventMulticaster。它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。
扩展点
SpringApplicationRunListener
- 一堆事件
2. Environment
运行时机:
核心代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建一个web运行环境或者标准运行环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置Environment对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置文件)
listeners.environmentPrepared(environment);
// 绑定ConfigurableEnvironment到当前的SpringApplication实例中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
1.getOrCreateEnvironment 根据环境判断创建的运行环境
getOrCreateEnvironment方法中根据WebApplicationType类型选择具体的Environment类型,也就是我们提到过的Servlet类型、Reative类型或者非Web应用类型。
- 增加servletConfigInitParams属性源和servletContextInitParams属性源,添加jndi属性源
- 增加两个属性源(系统属性源和系统环境属性源)主要有Java的版本号,Java虚拟机名称等
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
2.configureEnvironment
- 添加defaultProperties默认属性源(sources.addLast(new MapPropertySource(“defaultProperties”, this.defaultProperties));
- 命令属性源sources.addFirst(new SimpleCommandLinePropertySource(args));
- 解析命令的时候,判断是否以“–”开头
- 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置文件
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 添加初始的properties(注意:当前并未加载如application.properties/yml的properties)
configurePropertySources(environment, args);
// 添加初始的profile(注意:当前并未加载如application.properties/yml配置profile)
configureProfiles(environment, args);
}
- 发出事件ApplicationEnvironmentPreparedEvent,监听器为ConfigFileApplicationListener的onApplicationEvent方法
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 只触发Environment相关的事件
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
继续跟进
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
// 执行后置处理器
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
我们看到,首先加载了Environment的后置处理器,然后经过排序以后遍历触发每个处理器。这里注意,ConfigFileApplicationListener本身也实现了EnvironmentPostProcessor接口,所以这里将会触发ConfigFileApplicationListener内部方法执行
跟进ConfigFileApplicationListener的postProcessEnvironment方法
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
跟进addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
这里实例化了一个Loader用来加载application配置文件,而核心逻辑就在load方法当中。
EnvironmentPostProcessor
ConfigFileApplicationListener
收到 ApplicationEnvironmentPreparedEvent
事件后通过 SPI 加载所有的 EnvironmentPostProcessor
实现,触发其 postProcessEnviroment
方法。
SpringApplication.run() ->
SpringFactoriesLoader.loadFactories(ApplicationListener) ->
SpringApplication.prepareEnviroment() -> EventPublishingRunListener.enviromentPrepared(ApplicationEnviromentPraparedEvent) ->
SimpleApplicationEventMulticaster.multicastEvent() ->
ConfigFileApplicationListener.onApplicationOnEnviromentPreparedEvent() ->
EnviromentPostProcessor.postProcessEnviroment()
比较重要的 EnviromentPostProcessor
实现是 HostInfoEnvironmentPostProcessor
和 ConfigFileApplicationListener
。
- HostInfoEnvironmentPostProcessor.postProcessEnviroment
获取本机的 主机名和IP地址,封装在 PropertySource
添加到 environment 里。
- ConfigFileApplicationListener.postProcessEnviroment
ConfigFileApplicationListener
自身也实现了 EnvironmentPostProcessor
,通过内部类 Loader 去加载配置文件,其主要流程如下:
- 从 Environment 中获取 active 和 include 的 profile 集合。进行迭代:
- 获取所有的搜索路径,进行迭代,默认的搜索路径是
classpath:/,classpath:/config/,file:./,file:./config/
。 - 如果某个搜索路径不以
/
结尾的则认为是一个文件,直接加载,否则,找出所有的搜索文件名 name 进行迭代搜索,默认的搜索文件名是 “application”。 - 通过
PropertySourcesLoader
找出支持的所有配置文件后缀进行迭代。 - 最终得到
location + name + "-" + profile + "." + ext
组成的一个具体的完整路径,通过PropertiesLoader.load
方法加载该路径指向的配置文件。 PropertiesLoader.load
内部又根据配置文件的后缀用不同的PropertySourceLoader
去加载得到一个PropertySource
。- 对于解析得到的
PropertySource
,找出里面激活的 profile,添加到 proflie 集合里进行迭代。 - 继续迭代下一个 profile 。
- PropertySourceLoader
PropertySourceLoader
是用来加载 PropertySource
的一个策略接口,有两个具体的实现类 PropertiesPropertySourceLoader
和 YamlPropertySourceLoader
,前者用于加载 properties/xml
后缀的配置文件,后者用于加载 yml
后者的配置文件。
@ConfigurationProperties 使用的变量也是从environment中取的
spring boot默认可以加载当前启动路径{user.dir}下 /config目录中的配置文件, /home/config和docker没有关系, 在一些自动划构建工具可能用到这个特性
话不多说,直接下面看重点:
扩展点
Spring使用EnvironmentPostProcessor自定义启动配置项
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Map<String, Object> map = new HashMap<>();
map.put("localProperey", "value");
MapPropertySource target = (MapPropertySource) environment.getPropertySources().get(PROPERTY_SOURCE_NAME);
if (target == null) {
target = new MapPropertySource(PROPERTY_SOURCE_NAME, map);
}
environment.getPropertySources().addLast(target);
}
}
在META-INF的spring.factories中添加
org.springframework.boot.env.EnvironmentPostProcessor=\
com.xxxxx.MyEnvironmentPostProcessor
调用时间比使用BootstrapConfiguration(PropertySourceLocator的实现)更早
3. Banner
banner的位置在 resources目录下 默认为 banner.txt 和 banner命名的jpg\png\gif图片
- 加载图片banner,定义如下。如果是 banner.gif 或 banner.jpg 或banner.png 就会被加载 SpringApplicationBannerPrinter.getImageBanner
- 加载文字banner,默认是banner.txt文件 SpringApplicationBannerPrinter.getTextBanner
4. ConfigurableApplicationContext
反射获取注解上下文
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
5. SpringBootExceptionReporter
一般都是解析META-INF/spring.factories文件下
ApplicationContextInitializer、ApplicationListener、SpringBootExceptionReporter、SpringApplicationRunListeners等接口的实现类,基于SPI可插拔
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
只有一个实现FailureAnalyzers
一个Spring Boot 应用偶尔会因为某些原因启动失败,此时Spring Boot会友好地输出类似于这样一段文字,告诉你发生了什么,甚至应该采取什么行动:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.B required a bean of type 'com.example.A' that could not be found.
Action:
Consider defining a bean of type 'com.example.A' in your configuration.
这是怎么做到的呢?或许第一想法是SpringBoot会在出现问题的地方构造这样完整的异常和输出。实际上并非如此,Spring Boot提供了统一的接口进行问题的分析和诊断,这就是org.springframework.boot.diagnostics.FailureAnalyzer 接口。
扩展
如果做一个自己的组件,就可能会遇上需要处理异常并向使用者提供建议的情况。
我们定义一个异常
public class WannaStopException extends RuntimeException {
}
我们定义一个Bean在某(全)种(部)情况下会抛出异常
@Service
public class A {
public A() {
throw new WannaStopException();
}
}
我们定义一个FailureAnalyzer
专门处理这个WannaStopException
public class StopFailureAnalyzer extends AbstractFailureAnalyzer<WannaStopException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, WannaStopException cause) {
for (StackTraceElement stackTraceElement : cause.getStackTrace()) {
if (stackTraceElement.getClassName().equals("com.example.A")) {
return new FailureAnalysis("A想停止", "别要A了", cause);
}
}
return null;
}
}
AbstractFailureAnalyzer 帮助我们找到特定异常
接下来我们还要把StopFailureAnalyzer
放进spring.factories
中,在resources/META-INF/spring.factories(自己建)里添加
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.flowable.StopFailureAnalyzer
启动这个应用,就能看到如下输出
6. prepareContext 准备上下文
prepareContext 准备上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置上下文的environment
context.setEnvironment(environment);
// 应用上下文后处理
postProcessApplicationContext(context);
// 在context refresh之前,对其应用ApplicationContextInitializer
applyInitializers(context);
// 上下文准备(目前是空实现,可用于拓展)
listeners.contextPrepared(context);
// 打印启动日志和启动应用的Profile
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 向beanFactory注册单例bean:命令行参数bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 获取全部资源,其实就一个:SpringApplication的primarySources属性
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将bean加载到应用上下文中
load(context, sources.toArray(new Object[0]));
// 向上下文中添加ApplicationListener,并广播ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
- context.setEnvironment(environment)
之前我们的应用中有两个environment,一个在context中,一个在SpringApplication中。经过此方法后,就只会存在SpringApplication中的environment了,而context中的原environment会被回收。 - postProcessApplicationContext(context);
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
由于当前SpringApplication实例的属性:beanNameGenerator和resourceLoader都为null,所以此方法目前相当于什么也没做。此方法可能是我们定制SpringApplication所用
- applyInitializers(context);
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
// 解析当前initializer实现的ApplicationContextInitializer的泛型参数
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
// 断言context是否是requiredType的实例
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 向context应用初始化器
initializer.initialize(context);
}
}
在context refresh之前应用ApplicationContextInitializer到context中
DelegatingApplicationContextInitializer相当于context.initializer.classes的代理,最终还是会执行到被代理的initializer的initialize方法。
ContextIdApplicationContextInitializer
设置application id:从environment中获取spring.application.name配置项的值,并把设置成application id,若没有配置spring.application.name,则取默认值application;
将application id封装成ContextId对象,注册到beanFactory中。
ConfigurationWarningsApplicationContextInitializer
向上下文注册了一个BeanFactoryPostProcessor:ConfigurationWarningsPostProcessor实例;
实例化ConfigurationWarningsPostProcessor的时候,也实例化了它的属性Check[] checks,check中只有一个类型是ComponentScanPackageCheck的实例。
ServerPortInfoApplicationContextInitializer
向上下文注册了一个ApplicationListener:ServerPortInfoApplicationContextInitializer对象自己;
ServerPortInfoApplicationContextInitializer实现了ApplicationListener,所以他本身就是一个ApplicationListener。
SharedMetadataReaderFactoryContextInitializer
向context注册了一个BeanFactoryPostProcessor:CachingMetadataReaderFactoryPostProcessor实例。
ConditionEvaluationReportLoggingListener
将上下文赋值给自己的属性applicationContext;
向上下文注册了一个ApplicationListener:ConditionEvaluationReportListener实例;
从beanFactory中获取名为autoConfigurationReport的bean赋值给自己的属性report。
- listeners.contextPrepared(context);
还记得SpringApplicationRunListeners中listeners属性吗,没错,里面就一个EventPublishingRunListener对象。
调用EventPublishingRunListener的contextPrepared,发现其是空实现。
也就是相当于啥事也没做。
- listeners.contextLoaded(context);
一共过滤出5个监听器,他们的onApplicationEvent方法会被调用,具体做了如下事情:
ConfigFileApplicationListener
向context注册了一个BeanFactoryPostProcessor:PropertySourceOrderingPostProcessor实例;该实例后面会对我们的property sources进行重排序,另外该实例拥有上下文的引用。
LoggingApplicationListener
向beanFactory中注册了一个名叫springBootLoggingSystem的单例bean,也就是我们的日志系统bean。
BackgroundPreinitializer
目前什么也没做
DelegatingApplicationListener
目前什么也没做
EnableEncryptablePropertiesBeanFactoryPostProcessor
仅仅打印了一句debug日志,相当于什么也没做
7. refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
refresh((ApplicationContext) context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
这就是spring的上下文刷新逻辑了
```java
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh(); 这就是spring的上下文刷新逻辑了
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
```
AbstractApplicationContext.refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新
prepareRefresh();
// 通知子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 允许上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);
// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 注册DelegatingMessageSource
initMessageSource();
// 注册multicaster
initApplicationEventMulticaster();
// 创建内置的Servlet容器 tomcat等
onRefresh();
// 注册Listener
registerListeners();
// 完成BeanFactory初始化,初始化剩余单例bean
finishBeanFactoryInitialization(beanFactory);
// 发布对应事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
//ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
CommandLineRunner
CommandLineRunner 是很好的扩展接口,不是 Spring 框架原有的“宝贝”,它属于 SpringBoot 应用特定的回调扩展接口。源码如下所示:
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
CommandLineRunner 需要大家关注的其实就两点:
1)所有 CommandLineRunner 的执行时点在 SpringBoot 应用的 Application-Context 完全初始化开始工作之后(可以认为是 main 方法执行完成之前最后一步)。
2)只要存在于当前 SpringBoot 应用的 ApplicationContext 中的任何 Command-LineRunner,都会被加载执行(不管你是手动注册这个 CommandLineRunner 到 IoC 容器,还是自动扫描进去的)。
与其他几个扩展点接口类型相似,建议 CommandLineRunner 的实现类使用 @org.springframework.core.annotation.Order 进行标注或者实现 org.springframework.core.Ordered 接口,便于对它们的执行顺序进行调整,这其实十分重要,我们不希望顺序不当的 CommandLineRunner 实现类阻塞了后面其他 CommandLineRunner 的执行。