关联博文:
AbstractApplicationContext中refresh方法详解
Spring中refresh分析之prepareRefresh方法详解
Spring中refresh分析之obtainFreshBeanFactory方法详解
Spring中refresh分析之prepareBeanFactory方法详解
Spring中refresh分析之postProcessBeanFactory方法详解
Spring中refresh分析之invokeBeanFactoryPostProcessors方法详解
Spring中refresh分析之registerBeanPostProcessors方法详解
Spring中refresh分析之initMessageSource方法详解
Spring中refresh分析之initApplicationEventMulticaster方法详解
Spring中refresh分析之onRefresh方法详解
Spring中refresh分析之registerListeners方法详解
Spring中refresh分析之finishBeanFactoryInitialization方法详解
Spring中refresh分析之finishRefresh方法详解
本文我们开始逐个分析refresh方法内部的每一个方法。
方法功能梳理:
- 清理metadataReaderCache ;
- 设置close active的状态
- 准备环境ConfigurableEnvironment并触发其initPropertySources方法对servletContextInitParams做处理
- 验证标记为“必需”的所有属性是否可解析
- 初始化或者重置earlyApplicationListeners
- 初始化earlyApplicationEvents
【1】子类的prepareRefresh方法
如下所示首先调用AnnotationConfigServletWebServerApplicationContext
的prepareRefresh
方法,然后调用父类的prepareRefresh方法。
// AnnotationConfigServletWebServerApplicationContext#prepareRefresh
@Override
protected void prepareRefresh() {
// ClassPathBeanDefinitionScanner
this.scanner.clearCache();
super.prepareRefresh();
}
// ClassPathScanningCandidateComponentProvider
public void clearCache() {
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
metadataReaderCache 是一个Map,维护了Resource--MetadataReader
这样的信息。Resource表示资源比如class file信息,MetadataReader则维护了资源的获取与类元数据信息的获取等行为。
// CachingMetadataReaderFactory
public void clearCache() {
if (this.metadataReaderCache instanceof LocalResourceCache) {
synchronized (this.metadataReaderCache) {
// 这是Map<Resource, MetadataReader> metadataReaderCache
this.metadataReaderCache.clear();
}
}
else if (this.metadataReaderCache != null) {
// Shared resource cache -> reset to local cache.
setCacheLimit(DEFAULT_CACHE_LIMIT);
}
}
// CachingMetadataReaderFactory
public void setCacheLimit(int cacheLimit) {
if (cacheLimit <= 0) {
this.metadataReaderCache = null;
}
else if (this.metadataReaderCache instanceof LocalResourceCache) {
// 设置缓存数量限制
((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
}
else {
// 初始化metadataReaderCache
//LocalResourceCache extends LinkedHashMap<Resource, MetadataReader>
this.metadataReaderCache = new LocalResourceCache(cacheLimit);
}
}
总结this.scanner.clearCache();
就是如果metadataReaderCache 存在且是LocalResourceCache(其是一个LinkedHashMap),则调用clear方法进行清理。否则调用setCacheLimit方法完成metadataReaderCache 的初始化
或者缓存数量限制
。
【2】 AbstractApplicationContext的prepareRefresh
父类的方法如下所示:
protected void prepareRefresh() {
// Switch to active.
// 当前启动时间戳
this.startupDate = System.currentTimeMillis();
// 设置close active的状态
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...
//初始化或者重置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...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
首先是调用initPropertySources方法初始化资源配置,然后对必要的property进行校验,最后对earlyApplicationListeners
和earlyApplicationEvents
进行初始化。
这里说一下earlyApplicationEvents早期事件
当使用spring上下文发布事件的时候,其内部最终会用到
AbstractApplicationContext
下面这个属性来发布事件,但是可能此时applicationEventMulticaster
还没有创建好,是空对象,所以此时是无法广播事件的,那么此时发布的时间就是早期的时间,就会被放在this.earlyApplicationEvents
中暂时存放着,当事件广播器applicationEventMulticaster
创建好了之后,才会将早期的事件广播出去
本文环境下这里的applicationListeners如下所示:
0 = {ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener@4537}
1 = {RSocketPortInfoApplicationContextInitializer$Listener@4538}
2 = {ServerPortInfoApplicationContextInitializer@4539}
3 = {RestartApplicationListener@4540}
4 = {CloudFoundryVcapEnvironmentPostProcessor@4541}
5 = {ConfigFileApplicationListener@4542}
6 = {AnsiOutputApplicationListener@4543}
7 = {LoggingApplicationListener@4544}
8 = {BackgroundPreinitializer@4545}
9 = {ClasspathLoggingApplicationListener@4546}
10 = {DelegatingApplicationListener@4547}
11 = {ParentContextCloserApplicationListener@4548}
12 = {DevToolsLogFactory$Listener@4549}
13 = {ClearCachesApplicationListener@4550}
14 = {FileEncodingApplicationListener@4551}
15 = {LiquibaseServiceLocatorApplicationListener@4552}
【3】 initPropertySources
我们看一下initPropertySources
方法,其拿到应用的环境对象然后调用其initPropertySources
方法。
// GenericWebApplicationContext#initPropertySources
@Override
protected void initPropertySources() {
// StandardServletEnvironment ,如果environment为空则创建哦
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
}
}
这里我们看一下ConfigurableEnvironment
,其是StandardServletEnvironment
实例。如下所示,可以看到其维护了systemProperties、我们当前的profile(默认是default)、systemEnvironment、configurationProperties、我们的application.properties等环境与配置信息。
systemEnvironment
OriginTrackedMapPropertySource {name=‘applicationConfig: [classpath:/application.properties]’}
PropertiesPropertySource {name=‘systemProperties’}
继续往下看这里将会调用StandardServletEnvironment
的initPropertySources方法,如下所示其直接调用了WebApplicationContextUtils
的方法。
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
而WebApplicationContextUtils
的initServletPropertySources
方法呢,顾名思义其实就是用来处理servletContextInitParams
和servletConfigInitParams
的。如果servletContext不为null其将会实例化一个ServletContextPropertySource进行替换。如果servletConfig 不为null,其将会实例化一个ServletConfigPropertySource进行替换。
public static void initServletPropertySources(MutablePropertySources sources,
@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
// servletContextInitParams
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
// servletConfigInitParams
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
这里的sources指的是ConfigurationPropertySourcesPropertySource ,其内部维护了propertySourceList 其是一个CopyOnWriteArrayList:
private final List<PropertySource<?>> propertySourceList =
new CopyOnWriteArrayList<>();
// 内容如下所示
[ConfigurationPropertySourcesPropertySource {name='configurationProperties'},
StubPropertySource {name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'},
RandomValuePropertySource {name='random'},
OriginTrackedMapPropertySource {name='applicationConfig:[classpath:/application.properties]'},
MapPropertySource {name='devtools'}]
【4】 validateRequiredProperties
getEnvironment().validateRequiredProperties();
// AbstractEnvironment
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
this.propertyResolver.validateRequiredProperties();
}
如下所示PropertySourcesPropertyResolver
内维护了propertySources
、conversionService
等。
这个校验其实很简单,就是遍历Set<String> requiredProperties
判断propertySources中是否存在。
@Override
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
综上可以看到prepareRefresh就是准备了环境Environment、propertySource校验以及监听器与应用事件集合。