聊聊国际化MessageSource

栏目: Java · 发布时间: 5年前

内容简介:Spring对国际化这一块支持还是蛮友好的,上手也是蛮简单,但是加载流程还是需要大家掌握的,不然会少定义一个资源文件会让你莫名其妙的出现一些bug。接下来主要分享一下关于这一块的基本知识。顶层接口一共提供了三个获取信息的方法:在获取对应信息时,里面还有些许流程,我下面将会结合例子来进行说明,得先熟悉下该接口的主要实现类。

前言

Spring对国际化这一块支持还是蛮友好的,上手也是蛮简单,但是加载流程还是需要大家掌握的,不然会少定义一个资源文件会让你莫名其妙的出现一些bug。接下来主要分享一下关于这一块的基本知识。

MessageSource

public interface MessageSource {


    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);


    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;


    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}
复制代码

顶层接口一共提供了三个获取信息的方法:

  • 提供默认值 defaultMessage 参数,当根据 code 无法从相应的 ResourceBundle 中查询出数据时,会将 defaultMessage 的值返回。
  • 当根据 code 无法从相应的 ResourceBundle 中查询出数据时,直接抛出 NoSuchMessageException 异常。
  • 通过自定义 MessageSourceResolvable 解析器去获取信息, MessageSourceResolvable 也就是封装了 code , args , defaultMessage 三个参数,用法上并没有什么不同。只不过code参数为 String[] 数组形式,通过遍历调用的方式去获取信息,只要其中一个 code 能够获取到值,便直接返回。查询不出数据时且 defaultMessage 为空时,直接抛出 NoSuchMessageException 异常。

在获取对应信息时,里面还有些许流程,我下面将会结合例子来进行说明,得先熟悉下该接口的主要实现类。

聊聊国际化MessageSource

从类图结构中可以看出,顶层接口 MessageSource 下面有个抽象类 AbstractMessageSource ,三个基本实现类 ResourceBundleMessageSource , ReloadableResourceBundleMessageSource , StaticMessageSource

  • ResourceBundleMessageSource :支持对 .properties 的解析,解析完成后也是用map进行封装,最后数据存储在 PropertyResourceBundle 的成员变量 private Map lookup; 中。
  • ReloadableResourceBundleMessageSource: 可以解析 .properties.xml 文件,解析完成利用 PropertiesHolder 进行封装,底层还是 Properties 结构。
  • StaticMessageSource :这个相对来说最容易理解,内部就是直接用map封装了咱们需要的信息。

以上三个实现类均可以对返回信息做格式化处理。

下面我将拿 ResourceBundleMessageSource 这个类进行分析,先对其几个属性值进行分析一下:

  • alwaysUseMessageFormat :默认值为false,即默认不对返回信息做格式化处理。
  • useCodeAsDefaultMessage :默认值为false,设置成true时,当无法通过 code 参数返回信息时,会默认将 code 的值进行返回。
  • fallbackToSystemLocale :默认值为true,当根据 codelocale 参数无法获取对应的 ResourceBundle 时,会根据当前的环境设置获取 defaultLocale ,然后获取对应的 ResourceBundle

注入spring容器:

可以选择配置文件或者注解的方式进行配置到spring容器中,例如下方的注解方式:

@Bean
MessageSource messageSource() {
    ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
    resourceBundleMessageSource.setBasename("exception");
    resourceBundleMessageSource.setDefaultEncoding("UTF-8");
    return resourceBundleMessageSource;
}
复制代码

而spring 容器在初始化时,会在 refresh 方法中调用 initMessageSource 方法:

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        //从容器中获取name为 "messageSource",类型为MessageSource的bean
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Using MessageSource [" + this.messageSource + "]");
        }
    }
    else {
        DelegatingMessageSource dms = new DelegatingMessageSource();
        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 + "]");
        }
    }
}
复制代码

上面这段代码逻辑很清晰,先判断当前是否有该bean的BeanDefinition,若存在,则对MessageSource进行初始化并赋值给其成员变量 messageSource 。如果不存在该bean的BeanDefinition,则赋值一个空的MessageSource,也就是DelegatingMessageSource,以便能够正常的进行 getMessage 方法的调用。

具体使用:

有以下两种方式可以获取到容器中的 MessageResource :

//方式一:     
@Autowired
private MessageSource messageSource;

//方式二:
@Component
public class MessageResourceConfiguration implements MessageSourceAware {

    private MessageSource messageSource;

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
}
复制代码

PS:其实用 ApplicationContext 也可以, ApplicationContext 实现了 MessageSource 接口,并且在 refresh 方法中也对 messageSource 进行了注入,不熟悉的可以回顾下上面的 注入spring容器 环节。

使用案例:

如下图所示先定义好这三个配置文件(文件名需要和上面定义MessageSource的baseName属性值一致):

聊聊国际化MessageSource

exception.properties 我们称之为基类文件,它可以作为 exception_en.propertiesexception_zh.properties 两种配置文件的父类,联想一下spring的子父容器的概率,相信不难理解。

1.String app = messageSource.getMessage("0000", null, Locale.ENGLISH); //en_exception
2.String app = messageSource.getMessage("0001", null, Locale.ENGLISH); //base_exception
3.String app = messageSource.getMessage("0000", null, Locale.CHINESE); //zh_exception
4.String app = messageSource.getMessage("0000", null, Locale.JAPAN);   //zh_exception
复制代码

分别执行这四句代码,出现这个四个结果:

  1. 能够从 exception_en.properties 中获取”0000“对应的值:en_exception。
  2. 无法从 exception_en.properties 中获取”0001“对应的值,在其父类 exception.properties 中获取对应的值:base_exception。
  3. 能够从 exception_zh.properties 中获取”0000“对应的值:zh_exception。
  4. 无法获取 Locale.JAPAN 对应的 ResourceBundle ,但由于 fallbackToSystemLocale 值默认是true,所以会根据环境设置获取 defaultLocale ,然后获取对应的 ResourceBundle 。当前环境通过 System.getProperty("user.language") 去获取,目前是该值是 zh ,所以会从 exception_zh.properties 中获取”0000“对应的值:zh_exception。

假设在注入bean时将 fallbackToSystemLocale 改成false:

聊聊国际化MessageSource

运行上面的代码,会发现前面三个结果一致,但是最后一个结果有些许变化:

String app = messageSource.getMessage("0000", null, Locale.JAPAN);   //exception
复制代码

由于没有匹配到对应国家的 ResourceBundle ,直接从基类文件中获取结果。其实这里直接将 exception_zh.properties 配置文件移除也会获取相同的结果,都会从基类文件中获取。

假设在注入bean时将 useCodeAsDefaultMessage 改成true:

聊聊国际化MessageSource

运行代码:

String app = messageSource.getMessage("0003", null, Locale.CHINESE); //0003
复制代码

返回结果为“0003”,正是查询时用的 code 参数。“0003”不存在于咱们的 exception_zh.propertiesexception.properties 文件对应的 ResourceBundle 中,所以直接将 code 作为默认结果进行返回了。

总结:

对上面的基本流程进行简单的总结,可以分为三步:

  1. 检测是否有对应 locale 的配置,有的话则从当前 locale 配置中读取信息,并且可以追踪到基类文件中。
  2. 若没有对应 locale 的配置,如果 fallbackToSystemLocaletrue ,则获取默认 defaultLocale 的配置,并且可以追踪到基类文件中。
  3. 如果 fallbackToSystemLocalefalse ,则直接到基类文件中获取信息。

目前只是接触到根据不同的国家响应不同的异常状态码,后面会对页面这一块的国际化进行分享一下,SpringMVC也提供有对国际化这一块的支持。测试案例均在 github 中。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Open Data Structures

Open Data Structures

Pat Morin / AU Press / 2013-6 / USD 29.66

Offered as an introduction to the field of data structures and algorithms, Open Data Structures covers the implementation and analysis of data structures for sequences (lists), queues, priority queues......一起来看看 《Open Data Structures》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具