Spring 源码阅读 15:初始化 MessageSource

简介: 本文分析 Spring 上下文初始化过程中, MessageSource 初始化的过程。MessageSource 是 Spring 框架中,处理 i18n 的组件。

image.png

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 14:注册 BeanPostProcessor

前情提要

在之前的 ApplicationContext 初始化 Spring 容器 一文中,提到 AbstractApplicationContext#refresh 方法是一个非常重要的方法,它包含了 Spring 容器初始化的整个流程。最近的一系列文章都在深入分析这个方法中的每一个步骤的具体原理,本文接着分析 MessageSource 的初始化,也就是refresh方法中的这行代码:

// Initialize message source for this context.initMessageSource();

首先介绍一下 MessageSource 是什么。

MessageSource 是什么

在 Spring 中,MessageSource 主要用来处理国际化的问题,也就是常说的i18n(Internationalization)。

比如,我们开发的网站要适配多个国家和地区的语言,就需要使用不同的语言展示网站的内容,这事就需要用到i18n组件,MessageSource 就是 Spring 中的i18n组件。

MessageSource 本身是一个接口,接口定义如下:

publicinterfaceMessageSource {
@NullableStringgetMessage(Stringcode, @NullableObject[] args, @NullableStringdefaultMessage, Localelocale);
StringgetMessage(Stringcode, @NullableObject[] args, Localelocale) throwsNoSuchMessageException;
StringgetMessage(MessageSourceResolvableresolvable, Localelocale) throwsNoSuchMessageException;
}

这三个方法都是用来通过一个指定的标识符(code)以及语言信息获取特定消息的本地化文本。MessageSource 相关的重要的实现类以及他们之间的关系如下:

image.png

这里有一个重要的 HierarchicalMessageSource 子接口,提供了分层处理消息的功能。以下是接口的定义。

publicinterfaceHierarchicalMessageSourceextendsMessageSource {
voidsetParentMessageSource(@NullableMessageSourceparent);
@NullableMessageSourcegetParentMessageSource();
}

简而言之,就是给 MessageSource 提供了父子关系,开发者可以用一个父级的 MessageSource 处理公共的消息内容,子级的 MessageSource 只需要处理对应模块的消息内容即可。当通过子级的 MessageSource 获取不到消息内容是,就会到父级的 MessageSource 中获取。

接下来是两个比较典型的实现类,ResourceBundleMessageSource 和 StaticMessageSource。StaticMessageSource 是一个静态的消息源,需要提前硬编码内容,因此不是很常用。比较常用的是 ResourceBundleMessageSource,它从给定路径的消息文件中加载内容,供程序获取。

初始化 MessageSource

接下来看 Spring 初始化 MessageSource 的过程,也就是 initMessageSource 的代码。

protectedvoidinitMessageSource() {
ConfigurableListableBeanFactorybeanFactory=getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource=beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.if (this.parent!=null&&this.messageSourceinstanceofHierarchicalMessageSource) {
HierarchicalMessageSourcehms= (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() ==null) {
// Only set parent context as parent MessageSource if no parent MessageSource// registered already.hms.setParentMessageSource(getInternalParentMessageSource());
         }
      }
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource ["+this.messageSource+"]");
      }
   }
else {
// Use empty MessageSource to be able to accept getMessage calls.DelegatingMessageSourcedms=newDelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource=dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '"+MESSAGE_SOURCE_BEAN_NAME+"' bean, using ["+this.messageSource+"]");
      }
   }
}

首先,获取到当前上下文内部的 BeanFactory,之后整个方法体都是一个if-else语句块,判断条件是 BeanFactory 中是否包含名为MESSAGE_SOURCE_BEAN_NAME的 Bean。

publicstaticfinalStringMESSAGE_SOURCE_BEAN_NAME="messageSource";

也就是 Spring 在容器中查找 MessageSource 时使用的是固定的名称messageSource,因此,如果要在配置文件中配置 MessageSource,需要把bean标签的id属性设置为messageSource,才能被 Spring 作为消息源。如果我们在配置文件中配置了messageSource,则会执行if语句块的逻辑,否则执行else语句块的逻辑。下面分别来说分析。

如果beanFactory中有名为messageSource的 Bean,则获取到这个 Bean 的实例,并设置为当前上下文的 MessageSource。这里还有一个判断,如果获取到的messageSource实现了 HierarchicalMessageSource 接口,且当前上下文parent属性不为空,则将parentmessageSource赋值给messageSource作为父消息源。

如果我们没有在配置文件中配置 MessageSource,那么 Spring 就会创建一个,把它赋值给当前上下文的messageSource成员变量,并注册到beanFactory中。

这里创建的消息源类型为 DelegatingMessageSource,DelegatingMessageSource 的逻辑是,直接从父消息源获取消息,或者根据提供的内容生成默认的消息,否则就返回空或者抛出异常。它的作用就是,当当前上下文的 MessageSource 中的方法被调用的时候,有实现类来响应方法的调用,仅此而已。

后续

本文介绍的这部分内容比较简单,并且 MessageSource 在实际开发当中使用被使用率并不高。后续会接着分析 Spring 容器初始化的代码。

目录
相关文章
|
8天前
|
存储 Java 程序员
Spring 注册BeanPostProcessor 源码阅读
Spring 注册BeanPostProcessor 源码阅读
|
1天前
|
Java 数据库连接 Spring
Spring 整合 MyBatis 底层源码解析
Spring 整合 MyBatis 底层源码解析
|
2天前
|
Java Spring 容器
解读spring5源码中实例化单例bean的调用链
解读spring5源码中实例化单例bean的调用链
|
8天前
|
Java Spring
在Spring Boot中,可以通过控制`@PostConstruct`注解方法的执行顺序来实现初始化时的顺序控制
在Spring Boot中,可以通过控制`@PostConstruct`注解方法的执行顺序来实现初始化时的顺序控制
24 1
|
1天前
|
Java 程序员 Spring
Spring 源码阅读 一
Spring 源码阅读 一
|
1天前
|
Java 容器 Spring
Spring5源码解析5-ConfigurationClassPostProcessor (上)
Spring5源码解析5-ConfigurationClassPostProcessor (上)
|
2天前
|
缓存 Java 数据库连接
spring中注解驱动事务框架的源码
spring中注解驱动事务框架的源码
|
3天前
|
消息中间件 XML Java
经验大分享:spring项目在启动的时候执行方法初始化
经验大分享:spring项目在启动的时候执行方法初始化
|
7天前
|
XML Java 数据格式
Spring容器启动源码解析
Spring容器启动源码解析
|
3天前
|
Java
springboot自定义拦截器,校验token
springboot自定义拦截器,校验token
20 6