spring boot原理分析(九):上下文Context即世界2
- 前言
- 上下文准备
- 上下文刷新
- 附:
前言
上下文Context可以说spring boot中最重要的一个概念,不仅包含了tomcat和spring mvc的启动和管理,还对spring mvc原有模式中的bean注册进行了大幅简化,理解Spring boot的Context可以说是理解spring boot的基础。
原理分析(八)已经介绍了Spring boot的ApplicationContext的定义实现和创建,其中就包含了上下文Context携带了哪些必要的信息,能够起到哪些作用。本文主要对上下文的准备和刷新进行介绍。
首先还是对齐spring boot启动总流程中run方法的相对应的信息。在上下文Context构建完成之后,接下来就是对上下文的准备和刷新,如下代码所示。
......
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
......
上下文准备
prepareContext方法中实现了上下文相关的初始化。具体内容如下注释。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//1.设置环境
context.setEnvironment(environment);
//2.后处理上下文设置
postProcessApplicationContext(context);
//3.初始化上下文
applyInitializers(context);
//4.事件监听器发送上下文准备完毕事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//5.bean工厂中注册应用程序参数bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//6.bean工厂中注册条幅bean
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 7.注入基础类
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 8.事件监听器发送上下文加载完成事件
listeners.contextLoaded(context);
}
- 环境设置:上下文中包含了系统的环境相关的信息,主要是profile和properties配置,properties配置不仅包括项目内的properties文件,还有JVM system properties、操作系统环境变量等。
- 后处理上下文设置:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
//如果有,设置bean name生成器
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());
}
}
//设置Converter管理器
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
默认情况下,这里是没有设置bean name生成器和资源加载器,如果需要,可以自己设置。不过,最后的ConverterService是会被设置的。Converter组件是用来做参数转换的,比如String到日期的转换等,这些转换器都由ConversionService管理。
- 初始化上下文用到了ApplicationContextInitializer的子类实例:
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//实现上线文的初始化
initializer.initialize(context);
}
}
如果你还记得,上下文初始化子类的设置是在SpringApplication类的构造函数中完成的。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
上下文初始化子类,可以在上下文中设置一些必要的属性。
- 4和8一样,这部分已经在spring boot的事件监听中讲过了。
bean工厂中注册应用程序参数bean:将应用main中传入的参数设置为bean - bean工厂中注册条幅bean:spring boot条幅相关的在之前也讲过,就是开始打印的很大的spring boot。
- 注入基础类:这里获取了基础类,其中就包括入口类,然后分析了注解并注入到bean中。
上下文刷新
上下文刷新主要流程的逻辑不是在SpringApplication类中实现的。refreshContext中调用了refresh方法,而refresh方法回调了上下文Context的refresh方法。典型的我刷新我自己。
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
由于这里的实现是ServletWebServerApplicationContext,所以调用的是这个类的refresh方法,再往下,实际上
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
调用的是父类AbstractApplicationContext的refresh,正是在这个refresh方法中,定义了上下文刷新的主要流程步骤。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1.为上下文刷新作准备
prepareRefresh();
// 2.通知子类去刷新内部的bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3.准备bean工厂
prepareBeanFactory(beanFactory);
try {
//4. 允许bean工厂的后处理操作
postProcessBeanFactory(beanFactory);
//5. 触发bean工厂中注册的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
//6. 注册BeanPostProcessor处理器:.
registerBeanPostProcessors(beanFactory);
//7. 初始化MessageSource
initMessageSource();
//8. 初始化事件监听管理器
initApplicationEventMulticaster();
//9. 初始化Web服务器等(tomcat和spring mvc)
onRefresh();
//10. 检查事件监听处理器并注册
registerListeners();
//11. 实例化所有最后存在的单例bean
finishBeanFactoryInitialization(beanFactory);
//12. 启动Web服务器(tomcat和spring mvc)
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
- 为上下文刷新做准备:
主要是做了环境属性的配置,比如properties属性读入后存在一些占位但未确定的属性,这时就会被从context中读出的配置所取代,完成环境配置的最终更新。还会做一些事件监听器的初始化,这里的事件监听器是针对参考原理分析(七)事件的细化处理,比如LoggingApplicationListener就会监听多个已经讲过的事件。 - 通知子类去刷新内部的bean工厂:对beanFactory进行刷新并获取
- 准备bean工厂:
获取Context的相关设置,对beanFactory进行配置。
内部设置了bean的加载器、bean加载后处理器、事件发布器、环境变量、应用配置等等。 - 允许bean工厂的后处理操作:
这里主要设置了使用注解加载的bean的后处理器以及普通bean的后处理器。关于bean的后处理器就是之前提到的BeanPostProcessor。 - 触发bean工厂中注册的BeanFactoryPostProcessor:
BeanFactoryPostProcessor需要和4中的BeanPostProcessor区分开。BeanFactoryPostProcessor能够在bean创建前修改bean的定义属性。而BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。 - 注册BeanPostProcessor处理器:
这里代码上的注释和实际的内容有些出入,代码上的注释是注册阻止bean创建的处理器,但是源码里的实际内容是注册了各种处理器。可能是我理解有误,有待确定。 - 初始化MessageSource:
MessageSource是用来处理国际化的问题,这个之前有提到。 - 初始化事件监听管理器:
这里的事件监听管理器和后面的10的事件监听处理器,在原理分析(七)已经有涉及到。 - 初始化Web服务器等(tomcat和spring mvc)
这个方法是由子类ServletWebServerApplicationContext实现的
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
这里createWebServer方法是TomcatServletWebServerFactory.getWebServer方法,
初始化tomcat的容器。
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
这里就涉及到了Tomcat和spring mvc如何初始化的内容了。在我的tomcat + spring mvc原理系列文章中有比较详细的介绍。
- 实例化所有最后存在的单例bean
- 启动Web服务器(tomcat和spring mvc)
调用了tomcat的start方法,整个服务被启动了起来。
附:
spring boot的上下文管理是依托于spring的bean的注册和管理的基础上的,所以其中涉及了较多bean工厂和处理器相关的内容。如果想要有更加清楚的认识,需要深入了解spring的原理。