4.刷新上下文
这里对应的代码在run方法中如下:
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
题外话 - 优雅下线相关
这里的shutdownHook,是用来让Springboot服务优雅下线的。
什么叫优雅下线?一般来说,下线会用例如像直接kill进程等命令,显得很粗暴也不好管理;SpringBoot的优雅下线一般是通过调用接口,来关闭服务,这样子可以很简单地接到各个平台上,也没有那么粗暴了。
这个registerShutdownHook默认值为true,如果是通过例子那样通过context启动,而非自定义context的话,那么这个值在过程中没有被修改。
接下来来看看本期主角:这个refresh方法。
//ConfigurableApplicationContext
void refresh() throws BeansException, IllegalStateException;
这里的refresh被三个类实现了,其中两个和我们的这个AnnotationConfigServletWebServerApplicationContext有关:
- AbstractApplicationContext
- ServletWebServerApplicationContext
根据继承树,这里来看看最近的ServletWebServerApplicationContext:
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
实际上大体上依然是调用父类AbstractApplicationContext的refresh方法,只是在refresh方法抛出异常时,做了额外处理:关闭当前的webServer。
所以本期的重点就集中在这个AbstractApplicationContext的refresh方法上了,来看看代码:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
//......................
}
}
这里就很复杂了,此处先关注这几行,剩下的留到下一篇:
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
这里新建了一个StartupStep,根据上下文以及类的注解,这个类单纯是用来标注一下context并记录一下日志的辅助工具,不再关注。
接下来看看这里涉及的三个方法。
4.1 准备刷新
对应代码如下:
// Prepare this context for refreshing.
prepareRefresh();
//
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
上面就是做了一下简单的初始化工作:
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
设置变量,记录日志,没了。
4.1.1 初始化配置源
// Initialize any placeholder property sources in the context environment.
initPropertySources();
/**
* <p>Replace any stub property sources with actual instances.
* @see org.springframework.core.env.PropertySource.StubPropertySource
* @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources
*/
protected void initPropertySources() {
// For subclasses: do nothing by default.
}
这里是一个模板方法,根据提示我们找到了GenericWebApplicationContext里的对应方法:
/**
* {@inheritDoc}
* <p>Replace {@code Servlet}-related property sources.
*/
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
}
}
根据注解,这里就是将环境对象env中和servlet相关的变量替换掉。而之前在流程中并没有配置过这个servletContext,因此这里是个null,接下去其实也不执行了,因此此处不再细究。
4.1.2 验证环境配置
getEnvironment().validateRequiredProperties();
实际上这一步依然不执行,因此一样也不在细究。
4.1.3 监听者相关
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
根据之前的文章容易知道,applicationListener在Springboot也就是本系列文章的内容内是一个很关键的扩展点,大部分扩展事件都是通过这个来执行的。因此这里就是两个容器来回倒腾,总之这里就是让
earlyApplicationListeners和applicationListeners这俩兄弟务必一致咯。
最后就是初始化了earlyApplicationEvents,根据注解,一旦轮播者可用了事件就发出去了:
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
4.2 获取并刷新beanFactory
这里对应代码如下:
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
这里依然用的是模板方法,对应到的类是GenericApplicationContext。相信到这里还记得这个之前提过的类,实际上使用的context中的DefaultListableBeanFactory就是在这个父类构造方法中创建的。
不过这里的refresh显得很水,更像是打标记:
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
至于get,这里的重写方法就是普通的set、get封装了,不再赘述。
4.3 准备beanFactory
上一步很敷衍地刷新并获取了beanFactory,这里就用到了:
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
if (!shouldIgnoreSpel) {
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}
}
首先先给BeanFactory设置一个类加载器。由于之前没有设置这个变量,因此这里取得的是默认的类加载器。
如果没有设置忽略Spel表达式的选项,那么这里会给BeanFactory配置一个表达式解析器。
随后根据实际的需要,配置、忽略了一些相关的变量。这么多东西不是必须记忆的东西,就放到BeanFactory中进行细究吧。
总结
本篇文章主要解析了启动方法中刷新上下文中的:
- 准备其他工具,以及beanFactory为启动而做的特殊处理的步骤。
算是在刷新上下文这个启动过程最重要的步骤中的前菜了,这些工作属于在正式工作开始之前的统一准备处理。