public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();//准备工作
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
第一步
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
/**
* 准备此上下文以进行刷新、设置其启动日期和活动标志以及执行属性源的任何初始化。
*/
protected void prepareRefresh() {//准备刷新
// Switch to active.
// 切换active的处理状态,同时为了更加明确的表示出当前的Spring上下文状态也提供有一个closed标记
this.startupDate = System.currentTimeMillis(); // 获取开始时间
this.closed.set(false); // 启动的时候就需要将关闭状态设置为false
this.active.set(true); // 启动的时候将启动的状态设置为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.
// 在Spring容器里面可以通过“<context>”命名空间配置所有要加载的资源信息
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<>();// 准备进行事件的发布处理
}
面试题:请问在Spring容器里面启动耗时的统计操作是由那个方法发出开启的?
回答:在AbstractApplicatonContext类里面提供有一个startupDate属性,这个操作属性是在prepareRefresh()方法里面进行初始化定义的。
通过以上的准备刷新方法的源代码里面可以发现整个的Spring容器做了如下的几个操作准备:
进行一些标志位属性的定义(active、closed);
如果此时采用了DEBUG的日志模式,则要进行一些信息的输出;
考虑到项目里面会存在有一些资源配置(*.properties),所以需要进行所有资源配置的初始化处理;
通过Environment环境来实现一些属性的校验处理;
Spring之中提供有自定义事件的扩展机制,而此处就是进行事件的发布处理(事件监听集合可以在此处配置)。