Spring源码学习笔记(九、Spring启动流程解析:初始化消息源)

目录:

  • 什么是消息源
  • 如何使用消息源
  • Spring是如何实现消息源的

什么是消息源

Spring中定义一个MessageSource接口,以用于支持信息的国际化包含参数的信息替换

ApplicationContext接口扩展了MessageSource接口,因而提供了消息处理的功能(i18n或者国际化)。与HierarchicalMessageSource一起使用,还能够处理嵌套的消息,这些是Spring提供的处理消息的基本接口。

其定义如下:

 1 public interface MessageSource {
 2     // 用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息
 3     // args中的参数将使用标准类库中的MessageFormat来替换消息中的值
 4     String getMessage(String code, Object[] args, String default, Locale loc):
 5 
 6     // 与上一个方法相同,不同之处在于:没有指定默认值。如果没找到消息,会抛出一个NoSuchMessageException异常。
 7     String getMessage(String code, Object[] args, Locale loc) throws NoSuchMessageException;
 8 
 9     // 与上面的方法不同之处在于:将code、args等属性封装到MessageSourceResolvable中
10     String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
11 }

ResourceBundleMessageSource会用得更多一些。StaticMessageSource很少被使用,但能以编程的方式向源添加消息。

MessageSource类关系描述:

  • HierarchicalMessageSource:MessageSource子接口,提供解析层级消息的能力
    • 该接口的目的是设置父MessageSource用于当前对象无法解析消息时父MessageSource尝试解析
  • DelegatingMessageSource:空的MessageSource,它将所处理转发给父MessageSource。
    • 如果没有父MessageSource,它不会处理任何消息。
    • 用户未指定MessageSource时,Spring默认使用该类。
    • 该类的功能比较简单:将字符串和参数数组格式化为一个消息字符串
  • AbstractMessageSource:支持“配置文件”方式的抽象类,内部提供一个与区域设置无关的公共消息配置文件,消息代码为关键字。
  • AbstractResourceBasedMessageSource:基于资源并实现了MessageSource接口的抽象类,同时提供了一些参数配置的方法。
  • ResourceBundleMessageSource:该类通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。
    • 不同的区域获取加载资源文件,达到国际化信息的目的。
    • 该类依赖JDK的ResourceBundle,并结合了JDK MessageFormat提供的标准消息解析。
    • 被访问的ResourceBundle和为每个消息生成的MessageFormat都会被缓存。
  • ReloadableResourceBundleMessageSource:同ResourceBundleMessageSource。
    • 该类参与Spring ApplicationContex的资源加载。
    • 它使用java.util.Properties作为其消息的自定义数据结构,通过PropertiesPersister策略从Spring Resource中加载它们。
    • 该策略不仅能够根据时间戳的更改重新加载文件,而且还能够使用特定的字符编码加载属性文件。
  • StaticMessageSource:MessageSource的简单实现,允许以编程方式注册消息,提供基本的国际化支持(通常用于测试)

如何使用消息源

1、编写消息源(中文messageSource文件需要把中文转为Ascii

 1 # exceptions_en_US.properties文件
 2 exceptions.illegal = The user {0} attempted to login, time: {1}
 3  4 # exceptions_zh_CN.properties文件
 5 exceptions.illegal = u975eu6cd5u7528u6237{0}u5c1du8bd5u767bu5f55, u65f6u95f4uff1a{1}
 6  7 # message_en_US.properties文件
 8 id.ilegal = userid {0} is illegal! time: {1}
 9 password.empty = username{0}'s password is empty!
10 11 # message_zh_CN.properties文件
12 id.ilegal = u7528u6237u7f16u53f7{0}u975eu6cd5u767bu5f55uff01{1}
13 password.empty = u7528u6237u540d{0}u7684u5bc6u7801u4e0du80fdu4e3au7a7auff01

目录如下:

2、配置ResourceBundleMessageSource

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 7         <property name="basenames">
 8             <list>
 9                 <!-- 此处为消息源的目录 -->
10                 <value>message.exceptions</value>
11                 <value>message.message</value>
12             </list>
13         </property>
14     </bean>
15 
16 </beans>

3、测试

 1 public class MessageTest {
 2 
 3     public static void main(String[] args) {
 4         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("messageSource.xml");
 5 
 6         Object[] argArray = {"0001", new Date()};
 7         String message1 = ctx.getMessage("id.ilegal", argArray, Locale.CHINA);
 8         String message2 = ctx.getMessage("id.ilegal", argArray, Locale.US);
 9         System.out.println(message1);
10         System.out.println(message2);
11 
12         Object[] argArray2 = {"JDR"};
13         String message3 = ctx.getMessage("password.empty", argArray2, Locale.CHINA);
14         System.out.println(message3);
15     }
16 }

输出结果如下:

Spring是如何实现消息源的

 1 protected void initMessageSource() {
 2     // 获取BeanFactory
 3     ConfigurableListableBeanFactory beanFactory = getBeanFactory();
 4 
 5     // beanFactory中是否包含名字为messageSource的Bean
 6     if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
 7         // 创建MessageSource类型的Bean
 8         this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
 9         // 判断messageSource是否有parent MessageSource
10         if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
11             // 设置MessageSource的parent MessageSource.
12             HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
13             if (hms.getParentMessageSource() == null) {
14                 // 如果未注册任何父MessageSource,则仅将父上下文的MessageSource设置为父MessageSource
15                 hms.setParentMessageSource(getInternalParentMessageSource());
16             }
17         }
18         if (logger.isDebugEnabled()) {
19             logger.debug("Using MessageSource [" + this.messageSource + "]");
20         }
21     }
22     // beanFactory中不包含名字为messageSource的Bean
23     else {
24         // 新建DelegatingMessageSource对象
25         DelegatingMessageSource dms = new DelegatingMessageSource();
26         // 设置DelegatingMessageSource的parent MessageSource
27         dms.setParentMessageSource(getInternalParentMessageSource());
28         this.messageSource = dms;
29         // 向BeanFactory中注册MessageSource
30         beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
31         if (logger.isDebugEnabled()) {
32             logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
33                          "': using default [" + this.messageSource + "]");
34         }
35     }
36 }

从源码中可以得出如果可以的话,消息源id尽量定义为messageSource。

该段源码主要目的:

当一个ApplicationContext被加载时,它会自动在context中查找已定义为MessageSource类型的Bean。此Bean的名称须为messageSource

如果找到,那么所有对上述方法的调用将被委托给该Bean。否则ApplicationContext会在其父类中查找是否含有同名的Bean。如果有,就把它作为MessageSource。如果最终没有找到任何的消息源,实例化一个空的DelegatingMessageSource,使它能够接受上述方法的调用。

原文地址:https://www.cnblogs.com/bzfsdr/p/13034179.html