- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
6.6.3 初始化消息资源
在进行这段函数的解析之前,我们同样先来回顾 Spring 国际化的使用方法。
假设我们正在开发一个支持多国语言的 Web 应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的 i18n 国际化问题。对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。
“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾地区、中国香港地区的中文,还有新加坡地区的中文。Java 通过 java.util.Locale 类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。
java.util.Locale 是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础。下面给出几个创建本地化对象的示例:
//① 带有语言和国家/地区信息的本地化对象
Locale locale1 = new Locale("zh","CN");
//② 只有语言信息的本地化对象
Locale locale2 = new Locale("zh");
//③ 等同于 Locale("zh","CN")
Locale locale3 = Locale.CHINA;
//④ 等同于 Locale("zh")
Locale locale4 = Locale.CHINESE;
//⑤ 获取本地系统默认的本地化对象
Locale locale 5= Locale.getDefault();
JDK 的 java.util 包中提供了几个支持本地化的格式化操作工具类:NumberFormat、DateFormat、MessageFormat,而在 Spring 中的国际化资源操作也无非是对于这些类的封装操作,我们仅仅介绍下 MessageFormat 的用法以帮助大家回顾:
//①信息格式化串
String pattern1 = "{0},你好!你于{1}在工商银行存入{2} 元。";
String pattern2 = "At {1,time,short} On{1,date,long},{0} paid {2,number, currency}.";
//②用于动态替换占位符的参数
Object[] params = {"John", new GregorianCalendar().getTime(),1.0E3};
//③使用默认本地化对象格式化信息
String msg1 = MessageFormat.format(pattern1,params);
//④使用指定的本地化对象格式化信息
MessageFormat mf = new MessageFormat(pattern2,Locale.US);
String msg2 = mf.format(params);
System.out.println(msg1);
System.out.println(msg2);
Spring 定义了访问国际化信息的 MessageSource 接口,并提供了几个易用的实现类。MessageSource 分别被 HierarchicalMessageSource 和 ApplicationContext 接口扩展,这里我们主要看一下 HierarchicalMessageSource 接口的几个实现类,如图 6-3 所示。
图 6-3 MessageSource 类图结构
HierarchicalMessageSource 接口最重要的两个实现类是 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource。它们基于 Java 的 ResourceBundle 基础类实现,允许仅通过资源名加载国际化资源。ReloadableResourceBundleMessageSource 提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。StaticMessageSource 主要用于程序测试,它允许通过编程的方式提供国际化信息。而 DelegatingMessageSource 是为方便操作父 MessageSource 而提供的代理类。仅仅举例 ResourceBundleMessageSource 的实现方式。
(1)定义资源文件。
messages.properties(默认:英文),内容仅一句,如下:
test=test
messages_zh_CN.properties(简体中文):
test=测试
然后 cmd,打开命令行窗口,输入 native2ascii -encoding gbk C:\messages_zh_CN.properties C:\messages_zh_CN_tem.properties ,并将 C:\messages_zh_CN_tem.properties 中的内容替换到 messages_zh_CN.properties 中,这样 messages_zh_CN.properties 文件就存放的是转码后的内容了,比较简单。
(2)定义配置文件。
<bean id="messageSource" class="org.Springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>test/messages</value>
</list>
</property>
</bean>
其中,这个 Bean 的 ID 必须命名为 messageSource,否则会抛出 NoSuchMessageException 异常。
(3)使用。通过 ApplicationContext 访问国际化信息。
String[] configs = {"applicationContext.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
//①直接通过容器访问国际化信息
Object[] params = {"John", new GregorianCalendar().getTime()};
String str1 = ctx.getMessage("test",params,Locale.US);
String str2 = ctx.getMessage("test",params,Locale.CHINA);
System.out.println(str1);
System.out.println(str2);
了解了 Spring 国际化的使用后便可以进行源码的分析了。
在 initMessageSource 中的方法主要功能是提取配置中定义的 messageSource,并将其记录在 Spring 的容器中,也就是 AbstractApplicationContext 中。当然,如果用户未设置资源文件的话,Spring 中也提供了默认的配置 DelegatingMessageSource。
在 initMessageSource 中获取自定义资源文件的方式为 beanFactory.getBean(MESSAGE_ SOURCE_BEAN_NAME, MessageSource.class),在这里 Spring 使用了硬编码的方式硬性规定了子定义资源文件必须为 message,否则便会获取不到自定义资源配置,这也是为什么之前提到 Bean 的 id 如果部位 message 会抛出异常。
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
//如果在配置中已经配置了 messageSource ,那么将 messageSource 提取并记录在
this.messageSource 中
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.
class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof Hierarchical
MessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.
messageSource;
if (hms.getParentMessageSource() == null) {
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}else {
//如果用户并没有定义配置文件,那么使用临时的 DelegatingMessageSource 以便于作为调用
getMessage 方法的返回。
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME,
this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" +
MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}
通过读取并将自定义资源文件配置记录在容器中,那么就可以在获取资源文件的时候直接使用了,例如,在 AbstractApplicationContext 中的获取资源文件属性的方法:
public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessage
Exception {
return getMessageSource().getMessage(code, args, locale);
}
其中的 getMessageSource() 方法正是获取了之前定义的自定义资源配置。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论