在Spring context
与Spring IoC
对ApplicationContext
以及Context
相关的设计模式进行了介绍。
ApplicationContext
作为一个Context
在应用的运行层提供了IoC容器、事件、国际化等功能接口。
Spring
的国际化(i18n)功能是通过MessageSource
接口实现的,他提供了MessageSource::getMessage
方法从预设的资源中获取对应的数据。
(相关资料图)
在介绍
MessageSource
之前,得先说清楚Java(J2SE)对国际化的基本实现——ResourceBundle
,因为MessageSource
是用它实现的。ResourceBundle
很好理解,他就是按照规范的格式放置*.properties
资源文件,然后根据输入的语言环境来返回资源。
ResourceBundle
类提供了软件国际化的捷径。通过此类,可以使您所编写的程序可以:
- 轻松地本地化或翻译成不同的语言
- 一次处理多个语言环境
- 以后可以轻松地进行修改,支持更多的语言环境
说的简单点,这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。使用这个类,要注意的一点是,这个properties文件的名字是有规范的:一般的命名规范是: 自定义名_语言代码_国别代码.properties如果是默认的,直接写为:自定义名.properties比如:
myres_en_US.propertiesmyres_zh_CN.propertiesmyres.properties
当在中文操作系统下,如果myres_zh_CN.properties、myres.properties两个文件都存在,则优先会使用myres_zh_CN.properties,当myres_zh_CN.properties不存在时候,会使用默认的myres.properties。
ISO-8859-1
编码。Java Unicode Escape
格式。转换方法是通过JDK
自带的工具native2ascii
.classpath
的根目录(本例是放在src/main/resource
)中,文件名分别为:文件中的内容如下:
#i18n_en_US.propertiessay=Hallo world!#i18n_zh_CN.propertiessay=\u5927\u5BB6\u597D\uFF01#i18n_web_BASE64.propertiessay=+-+-+-ABC
public class I18nApp {public static void main(String[] args) { //使用当前操作系统的语言环境ResourceBundle rb = ResourceBundle.getBundle("i18n", Locale.getDefault());System.out.println(rb.getString("say")); //指定简体中文环境 rb = ResourceBundle.getBundle("i18n", new Locale("zh", "CN"));System.out.println(rb.getString("say")); //通过预设指定简体英文环境rb = ResourceBundle.getBundle("i18n", Locale.SIMPLIFIED_CHINESE);System.out.println(rb.getString("say")); //指定美国英语rb = ResourceBundle.getBundle("i18n", Locale.US);System.out.println(rb.getString("say")); //使用自定义的语言环境Locale locale = new Locale("web", "BASE64");rb = ResourceBundle.getBundle("i18n", locale);System.out.println(rb.getString("say"));}}
按照开发文档的要求:
ResourceBundle
加载的资源文件都必须放置在根目录
${resourceName}_${language}_${region}
的方式来命名。这个命名方式正好能对应ResourceBundle::getBundle
方法中的参数。例如:
ResourceBundle.getBundle("i18n", new Locale("zh", "CN"))
- "i18n"对应
${name}
- "zh"对应
${language}
- 而“CN”对应$
这样我们就可以通过传导参数来使用不同的资源。如果不指定
${language}
和${region}
,该文件就是一个默认文件。
Locale
类预设了很多资源类型,比如:Locale.SIMPLIFIED_CHINESE
、Locale.US
,实际上他们就等价于new Locale("zh", "CN")
和new Locale("en", "US")
。只是Java的开发人员做了一些静态的预设。
除了预设内容的Locale
,我们还可以像Locale locale = new Locale("web", "BASE64")
这样添加自定义的内容,他对应名为i18n_web_BASE64.properties
的资源文件。
定义三个资源文件,放到src的根目录下面(必须这样,或者你放到自己配置的calsspath下面。
bbb=thanks
aaa=goodbbb=thanks
aaa=\u597dbbb=\u591a\u8c22
import java.util.Locale;import java.util.ResourceBundle;/*** 国际化资源绑定测试** @author leizhimin 2009-7-29 21:17:42*/public class TestResourceBundle {public static void main(String[] args) {Locale locale1 = new Locale("zh", "CN");ResourceBundle resb1 = ResourceBundle.getBundle("myres", locale1);System.out.println(resb1.getString("aaa"));ResourceBundle resb2 = ResourceBundle.getBundle("myres", Locale.getDefault());System.out.println(resb1.getString("aaa"));Locale locale3 = new Locale("en", "US");ResourceBundle resb3 = ResourceBundle.getBundle("myres", locale3);System.out.println(resb3.getString("aaa"));}}
运行结果:
好好goodProcess finished with exit code 0
如果使用默认的Locale
,那么在英文操作系统上,会选择myres_en_US.properties
或myres.properties
资源文件。
Locale
对象表示了特定的地理
、政治
和文化地区
。需要 Locale 来执行其任务的操作称为语言环境敏感的操作,它使用 Locale 为用户量身定制信息。例如,显示一个数值就是语言环境敏感的操作,应该根据用户的国家、地区或文化的风俗/传统来格式化该数值。使用此类中的构造方法来创建 Locale:
语言参数是一个有效的 ISO 语言代码。这些代码是由 ISO-639 定义的小写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:http://www.loc.gov/standards/iso639-2/englangn.html。国家参数是一个有效的 ISO 国家代码。这些代码是由 ISO-3166 定义的大写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html。
如果觉得麻烦,可以直接将中文粘贴到里面,回车就可以看到转码后的结果了。
ApplicationContext
接口扩展了MessageSource
接口,因而提供了消息处理的功能(i18n
或国际化
)。与HierarchicalMessageSource
一起使用,它还能够处理嵌套的消息,这些是spring提供的处理消息的基本接口。
让我们快速浏览一下它所定义的方法:
用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。args中的参数将使用标准类库中的MessageFormat来作消息中替换值。
本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoSuchMessageException异常。
上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定MessageSourceResolvable实现。
ApplicationContext
被加载时,它会自动在context
中查找已定义为MessageSource
类型的bean。此
bean
的名称须为messageSource
。
如果找到,则:所有对上述方法的调用将被委托给该bean
。
否则,ApplicationContext
会在其父类中查找是否含有同名的bean
。如果有,就把它作为MessageSource
。
如果它最终没有找到任何的消息源,一个空的StaticMessageSource
将会被实例化,使它能够接受上述方法的调用。
Spring
目前提供了两个MessageSource
的实现:
它们都继承
NestingMessageSource
以便能够处理嵌套的消息。StaticMessageSource
很少被使用,但能以编程方式向消息源(Message Source)添加消息(Message)。
ResourceBundleMessageSource
会用得更多一些,为此提供了一下实例化的示例:
format exceptions windows
// 资源文件ApplicationResources.properties放在src目录下,也就是classes目录// 配置文件messages.xml也放在src目录下,也就是classes目录// 第1种方法MessageSource resources = new ClassPathXmlApplicationContext("messages.xml");String msg = resources.getMessage("XXXXX", new String[] { "OOOOO" }, null);// 第2种方法ReloadableResourceBundleMessageSource messageSource;messageSource = new ReloadableResourceBundleMessageSource();messageSource.setBasename("classpath:/ApplicationResources");messageSource.setUseCodeAsDefaultMessage(true);msg = messageSource.getMessage("XXXXX", new String[] { "OOOOO" }, Locale.CHINA);// 第3种方法 | 此方式中, messageSource Bean 是配置在 spring-servlet.xml 中的WebApplicationContext wac = RequestContextUtils.getWebApplicationContext(request);msg = wac.getMessage("XXXXX", new String[] { "OOOOO" }, Locale.CHINA);
ResourceBundleMessageSource
: 提供国际化的类。
ResourceBundleMessageSource
的底层实现依赖于java.util.ResourceBundle
说的简单点,这个类的作用就是读取资源属性文件(.properties
)然后,根据.properties
文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定)然后,获取相应的properties
文件的内容。
ResourceBundleMessageSource
的功能就是用Java标准库的ResourceBundle
实现的,所以使用起来和ResourceBundle
也差不多。
@Configurationpublic class I18nApp {@Bean("messageSource")ResourceBundleMessageSource resourceBundleMessageSource() {ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();messageSource.setBasenames(new String[] { "i18n", "extend" });//添加资源名称return messageSource;}}
或
i18n extend
切记一定要标记id=messageSource
。basenames
这个Setter
用于指定*.properties
资源文件的名称,规则和前面介绍的ResourceBundle
一样。
ApplicationContext::getMessage
方法获取对应的资源了:@Configurationpublic class I18nApp {@Bean("messageSource")ResourceBundleMessageSource resourceBundleMessageSource() {ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();messageSource.setBasenames(new String[] { "i18n", "extend" });return messageSource;}public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(I18nApp.class);System.out.println("Spring Default 1:" + context.getMessage("say", null, Locale.getDefault()));System.out.println("Spring Default 2:" + context.getMessage("say", null, null));System.out.println("Spring Chinese:" + context.getMessage("say", null, Locale.SIMPLIFIED_CHINESE));System.out.println("Spring Us English:" + context.getMessage("say", null, Locale.US));System.out.println("Spring Custom:" + context.getMessage("say", null, new Locale("web", "BASE64")));System.out.println("Spring Argument:" + context.getMessage("info", new String[] {"chkui"},null));System.out.println("Spring Info:" + context.getMessage("say", null, null));}}
# i18n_en_US.propertiesmessage=welcome:{0}# i18n_zh_CN.propertiesmessage=欢迎:{0}
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("SpringMVC.xml"); System.out.println(context.getMessage("message", new Object[]{"lgh"}, Locale.SIMPLIFIED_CHINESE)); System.out.println(context.getMessage("message", new Object[]{"lgh"}, Locale.US)); }
@Controllerpublic class TestController { @Autowired private ResourceBundleMessageSource messageSource; @RequestMapping(value = "/test4", method = RequestMethod.GET) public String test4(Locale locale) { System.out.println(messageSource.getMessage("message", null, locale)); return "i18n"; }}
注意上面的示例代码的这一行:
context.getMessage("info", new String[] {"chkui"},null))
这里的getMessage
向方法传递了一个数组,它用于替换资源文件
中的占位符号。
在例子中我们除了
i18n
还加载了一个extend.properties
文件,文件内容如下:
info={0}\u5E05\u7684\u8BA9\u4EBA\u6CA1\u813E\u6C14\u3002
文件中的{0}
表示这个位置用数组中的[0]
位置的元素替换。还有一点需要注意的是,*.properties
文件输入中文等UTF-8
的符号时需要保留上面这种ACSII
的格式,现在大部分IDE都会自动处理的,切记不要为了方便看内容将*.properties
的编码格式切换为UTF-8
。
我们有三种方式获取MessageSource接口:
//直接使用ApplicationContext context = new AnnotationConfigApplicationContext(I18nApp.class);context.getMessage("say", null, Locale.getDefault()));//MessageSourceAware(ApplicationContextAware)接口public class ExtendBean implements MessageSourceAware {@Overridepublic void setMessageSource(MessageSource messageSource) {this.setterMs = messageSource;}}//从容器直接注入public class ExtendBean implements MessageSourceAware {@Autowiredprivate MessageSource autowiredMs;}
需要注意的是,使用@Autowired等方式直接获取MessageSource类型的数据得到的是添加到容器的那个Bean,而其他方式获取到的是ApplicationContext。
标签: