Spring中refresh分析之prepareRefresh方法详解

简介: Spring中refresh分析之prepareRefresh方法详解





本文我们开始逐个分析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进行校验,最后对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等环境与配置信息。


13713731838248aeac8897014b596f61.png

systemEnvironment

OriginTrackedMapPropertySource {name=‘applicationConfig: [classpath:/application.properties]’}


9abcad0b064a4a20b0386bc2fb0b957d.png

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 内维护了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校验以及监听器与应用事件集合。


目录
相关文章
|
20天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
46 14
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
34 1
|
2月前
|
存储 安全 Java
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
34 1
|
2月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
597 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
28 0
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
226 2
|
3月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
162 5
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
230 2