本文我们开始逐个分析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校验以及监听器与应用事件集合。