4-BeanFactory的增强:ApplicationCont
2020-02-25 本文已影响0人
鹏程1995
概要
过度
上面我们宏观介绍了refresh()
的基本思路:
接下来我们按照框出来的四个部分依次介绍,本节介绍第一部分:启动前准备及验证。
内容简介
本文介绍ApplicationContext
在调用刷新操作前的准备工作,主要涉及一些启动环境的校验、标记量的设置和准备工作。
所属环节
启动前准备及验证。
上下环节
上文: ApplicationContext
已存在,调用refresh()
重新载入;或者项目启动,加载配置文件后调用refresh()
。
下文: 开始进行刷新(先进行BeanFactory
的相关设置)
源码解析
入口
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 准备此 ApplicationContext
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// BeanFactory 的初始化及对应的 BD 的加载
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 对 BeanFactory 中的一些和上下文相关的属性进行设置和同步。
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 定义了一个钩子,供子类实现
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用 BeanFactory 的后处理器对其进行处理
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册那些拦截 Bean 创建的后处理器
// 注意,上面 invokeBeanFactoryPostProcessors() 各种实例化各种注册,操作的都是针对 factory 的后处理器。这里才是针对 bean
// 的后处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化本 context 的 Message 源【国际化和本土化】【将对应的信息符号转化成对应的语言提示】
// TODO 没啥需要,最后再看
initMessageSource();
// Initialize event multicaster for this context.
// 初始化事件广播相关的东西
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 子实现类根据各自情况实现其他的特殊 Bean 的处理
// 算是留下的一个钩子
onRefresh();
// Check for listener beans and register them.
// 找出所有可注册的监听器并注册成有效的监听器,完成初始化后将之前缓存的事件散发出去
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 将剩余的非懒加载的单例加载一下
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成启动,散发相应事件
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();
}
}
}
还是之前的refresh()
函数。我们主要介绍其中的prepareRefresh()
方法。
刷新前的准备
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
// 准备此 ApplicationContext
// 设置启动时间和启动标志,初始化相应属性【什么事件、监听器之类的】
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment.
// 根据此 ApplicationContext 的上下文环境初始化对应的属性
// 默认不干啥,留给子类覆盖
// 可以和下一句呼应,也就是说根据环境设置必须设置的属性,如果没有,正好下一句直接检测报错
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
// 校验 ConfigurableEnvironment 中设置的必有属性是否有设置
// 可以自己在前面的钩子中调用 getEnvironment().setRequiredProperties() 设置子项目要必须
// 设置的属性
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
// ApplicationListeners 看样子只加不删,每次都会把旧的拷贝过去
// TODO 这里的逻辑看看怎么回事,结合关闭操作代码吧。感觉要借助 earlyApplicationListeners 做个左手倒右手的事情
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...
// 将存储提前到来的事件的set准备好,初始化完成后会进行广播
this.earlyApplicationEvents = new LinkedHashSet<>();
}
主要做了三件事:
- 设置启动标志量、记录启动时间点
- 进行环境必配置变量的验证
- 对监听器、事件存储进行刷新
其中我们主要介绍第二点。
环境验证
getEnvironment().validateRequiredProperties()
是调用Environment
的校验函数,如果有必设的值没有设置,会直接抛出异常结束。
我们一般会先设置哪些属性是必须设置的,然后再调用这个方法进行验证。如果要用的话,我们一般用前一句的钩子设置必须设的属性。