• 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,使它能够接受上述方法的调用。

  • 相关阅读:
    LeetCode——37. 解数独
    LeetCode ——42. 接雨水
    异常
    IO/FILE
    函数与模块
    选择与循环
    运算符
    字符串、列表、元组等
    SVTyper
    Error:Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-not69mld/pysam/
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/13034179.html
Copyright © 2020-2023  润新知