Spring中refresh分析之prepareRefresh方法详解

关联博文:
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方法

如下所示首先调用AnnotationConfigServletWebServerApplicationContextprepareRefresh方法,然后调用父类的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进行校验,最后对earlyApplicationListenersearlyApplicationEvents 进行初始化。

这里说一下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);
}

WebApplicationContextUtilsinitServletPropertySources方法呢,顾名思义其实就是用来处理servletContextInitParamsservletConfigInitParams的。如果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 内维护了propertySourcesconversionService等。
在这里插入图片描述

这个校验其实很简单,就是遍历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校验以及监听器与应用事件集合。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值