Java国际化/本地化实战(下)

简介: Java国际化/本地化实战(下)

MessageSource

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法:

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

code表示国际化资源中的属性名;args用于传递格式化串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;locale表示本地化对象;

String getMessage(String code, Object[] args, Locale locale)

throws NoSuchMessageException

与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常;

String getMessage(MessageSourceResolvable resolvable, Locale locale)

throws NoSuchMessageException

MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个接口方法相同。


MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类,如图5-7所示:


HierarchicalMessageSource接口添加了两个方法,建立父子层级的MessageSource结构,类似于前面我们所介绍的HierarchicalBeanFactory。该接口的setParentMessageSource (MessageSource parent)方法用于设置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。


HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它们基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源。ReloadableResourceBundleMessageSource提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。StaticMessageSource主要用于程序测试,它允许通过编程的方式提供国际化信息。而DelegatingMessageSource是为方便操作父MessageSource而提供的代理类。


ResourceBundleMessageSource


该实现类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。

通过JDK的基础类完成了本地化的操作,下面我们使用ResourceBundleMessageSource来完成相同的任务。读者可以比较两者的使用差别,并体会Spring所提供的国际化处理功能所带给我们的好处:

通过ResourceBundleMessageSource配置资源


<bean id="myResource"  
class="org.springframework.context.support.ResourceBundleMessageSource">  
    <!--①通过基名指定资源,相对于类根路径-->  
    <property name="basenames">    
       <list>  
          <value>com/baobaotao/i18n/fmt_resource</value>  
       </list>  
    </property>  
  </bean>   

启动Spring容器,并通过MessageSource访问配置的国际化资源,如代码清单 5 19所示:

String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
//①获取MessageSource的Bean  
MessageSource ms = (MessageSource)ctx.getBean("myResource");   
Object[] params = {"John", new GregorianCalendar(). getTime()};  
//②获取格式化的国际化信息  
String str1 = ms.getMessage("greeting.common",params,Locale.US);  
String str2 = ms.getMessage("greeting.morning",params,Locale.CHINA);  
String str3 = ms.getMessage("greeting.afternoon",params,Locale.CHINA);  
System.out.println(str1);  
System.out.println(str2);  
System.out.println(str3);  

比较代码清单5-19中的代码,我们发现最主要的区别在于我们无须再分别加载不同语言、不同国家/地区的本地化资源文件,仅仅通过资源名就可以加载整套的国际化资源文件。此外,我们无须显式使用MessageFormat操作国际化信息,仅通过MessageSource# getMessage()方法就可以完成操作了。这段代码的运行结果与代码清单5 17的运行结果完全一样。


ReloadableResourceBundleMessageSource


前面,我们提到该实现类比之于ResourceBundleMessageSource的唯一区别在于它可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响。这时,通过该实现类就可以解决国际化信息更新的问题。请看下面的配置:

<bean id="myResource"   
lass="org.springframework.context.support. ReloadableResourceBundleMessageSource">  
   <property name="basenames">  
      <list>  
        <value>com/baobaotao/i18n/fmt_resource</value>  
      </list>  
   </property>  
   <!--① 刷新资源文件的周期,以秒为单位-->  
   <property name="cacheSeconds" value="5"/>   
 </bean>  

在上面的配置中,我们通过cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。


我们编写一个测试类对上面配置的ReloadableResourceBundleMessageSource进行测试:

String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
MessageSource ms = (MessageSource)ctx.getBean("myResource");  
Object[] params = {"John", new GregorianCalendar().getTime()};  
for (int i = 0; i < 2; i++) {  
    String str1 = ms.getMessage("greeting.common",params,Locale.US);      
    System.out.println(str1);  
    Thread.currentThread().sleep(20000); //①模拟程序应用,在此期间,我们更改资源文件   
}  

在①处,我们让程序睡眠20秒钟,在这期间,我们将fmt_resource_zh_CN.properties资源文件的greeting.common键值调整为:

---How are you!{0},today is {1}---

我们将看到两次输出的格式化信息分别对应更改前后的内容,也即本地化资源文件的调整被自动生效了:

How are you!John,today is 1/9/07 4:55 PM
---How are you!John,today is 1/9/07 4:55 PM---

容器级的国际化信息资源


在如图5-7所示的MessageSource类图结构中,我们发现ApplicationContext实现了MessageSource的接口。也就是说ApplicationContext的实现类本身也是一个MessageSource对象。


将ApplicationContext和MessageSource整合起来,乍一看挺让人费解的,Spring这样设计的意图究竟是什么呢?原来Spring认为:在一般情况下,国际化信息资源应该是容器级。我们一般不会将MessageSource作为一个Bean注入到其他的Bean中,相反MessageSource作为容器的基础设施向容器中所有的Bean开放。只要我们考察一下国际化信息的实际消费场所就更能理解Spring这一设计的用意了。国际化信息一般在系统输出信息时使用,如Spring MVC的页面标签,控制器Controller等,不同的模块都可能通过这些组件访问国际化信息,因此Spring就将国际化消息作为容器的公共基础设施对所有组件开放。


既然一般情况下我们不会直接通过引用MessageSource Bean使用国际信息,那如何声明容器级的国际化信息呢?我们其实在5.1.1节讲解Spring容器的内部工作机制时已经埋下了伏笔:在介绍容器启动过程时,我们通过代码清单5-1对Spring容器启动时的步骤进行剖析,④处的initMessageSource()方法所执行的工作就是初始化容器中的国际化信息资源:它根据反射机制从BeanDefinitionRegistry中找出名称为“messageSource”且类型为org.springframework.context.MessageSource的Bean,将这个Bean定义的信息资源加载为容器级的国际化信息资源。请看下面的配置:

<!--①注册资源Bean,其Bean名称只能为messageSource -->  
<bean id="messageSource"   
      class="org.springframework.context.support.ResourceBundleMessageSource">  
  <property name="basenames">  
     <list>  
       <value>com/baobaotao/i18n/fmt_resource</value>  
     </list>  
  </property>  
</bean>  

下面,我们通过ApplicationContext直接访问国际化信息,如代码清单5 23所示:

String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
//①直接通过容器访问国际化信息  
Object[] params = {"John", new GregorianCalendar().getTime()};  
String str1 = ctx.getMessage("greeting.common",params,Locale.US);  
String str2 = ctx.getMessage("greeting.morning",params,Locale.CHINA);     
System.out.println(str1);  
System.out.println(str2);  

运行以上代码,输出以下信息:

How are you!John,today is 1/9/07 5:24 PM
早上好!John,现在是下午5:24

假设MessageSource Bean名字没有命名为“messageSource”,以上代码将抛出NoSuchMessageException异常。

参考

  • Spring 4.x企业应用开发实战
目录
相关文章
|
2月前
|
安全 Java 开发者
告别NullPointerException:Java Optional实战指南
告别NullPointerException:Java Optional实战指南
282 119
|
4月前
|
缓存 前端开发 Java
基于最新 Java 技术栈的在线任务管理系统开发实战详解
本项目基于最新Java技术栈开发在线任务管理系统,涵盖任务创建、分配、跟踪、统计等功能。采用Spring Boot 3.2.x、React 18、PostgreSQL 16等主流技术,详解项目架构设计、核心功能实现及部署流程,助力掌握现代Java全栈开发技能。
324 6
|
4月前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
652 3
|
3月前
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
445 0
|
4月前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
977 1
|
3月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
475 100
|
4月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
277 7
|
4月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
4月前
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
882 0
|
5月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
183 0