ApplicationContext 接口扩展了一个名为 MessageSource 的接口,因此,它提供了国际化(「i18n」)功能。Spring 还提供了 HierarchicalMessageSource 接口,它可以分层次地解析消息。这些接口共同提供了Spring 实现消息解析的基础。在这些接口上定义的方法包括:
String getMessage(String code, Object[] args, String default, Locale loc)
:用于从 MessageSource 中获取消息的基本方法。当没有为指定的 locale 找到消息时,会使用默认的消息。任何传入的参数都成为替换值,使用标准库提供的 MessageFormat 功能。String getMessage(String code, Object[] args, Locale loc)
:基本上与前一个方法相同,但有一点不同。不能指定默认消息。如果找不到消息,会抛出一个 NoSuchMessageException。String getMessage(MessageSourceResolvable resolvable, Locale locale)
:前面的方法中使用的所有属性也被包装在一个名为 MessageSourceResolvable 的类中,你可以使用这个方法。
当 ApplicationContext 被加载时,它会自动搜索定义在上下文中的 MessageSource bean。这个 Bean 必须有 messageSource 这个名字。如果找到了这样的 Bean,所有对前面的方法的调用都被委托给消息源。如果没有找到消息源,ApplicationContext 会尝试找到一个包含有相同名称的 Bean 的父类。如果找到了,它就使用该 bean 作为消息源。如果 ApplicationContext 不能找到任何消息源,那么就会实例化一个空的 DelegatingMessageSource,以便能够接受对上面定义的方法的调用。
Spring 提供了三种 MessageSource 实现:ResourceBundleMessageSource、ReloadableResourceBundleMessageSource 和 StaticMessageSource。它们都实现了 HierarchicalMessageSource,以便进行嵌套消息传递。StaticMessageSource 很少被使用,但它提供了向消息源添加消息的程序化方法。下面的例子显示了 ResourceBundleMessageSource:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
这个例子假设你在 classpath 中定义了三个资源包,分别是 format、exceptions 和 windows。任何解析消息的请求都以 JDK 标准的方式处理,即通过 ResourceBundle 对象解析消息。就本例而言,假设上述两个资源包文件的内容如下:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
下一个例子显示了一个运行 MessageSource 功能的程序。请记住,所有 ApplicationContext 的实现也是 MessageSource 的实现,所以可以投到 MessageSource 接口:
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}
上述程序的结果输出如下:
Alligators rock!
简而言之,MessageSource 被定义在一个叫做 beans.xml 的文件中,它存在于你的 classpath 的根部。messageSource Bean 定义通过它的 basenames 属性引用了一些资源包。在列表中传递给 basenames 属性的三个文件作为文件存在于你的 classpath 根部,分别被称为 format.properties、exceptions.properties 和 windows.properties。
下一个例子显示了传递给消息查询的参数。这些参数被转换为 String 对象,并被插入到查找消息的占位符中:
<beans>
<!-- 这个 MessageSource 被用在一个 Web 应用程序中 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- 让我们把上述的 MessageSource 注入这个 POJO 中 -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}
execute 方法执行后输出如下
The userDao argument is required.
关于国际化(「i18n」),Spring 的各种 MessageSource 实现遵循与标准 JDK ResourceBundle 相同的区域划分和回退规则。简而言之,继续使用之前定义的 messageSource 示例,如果你想根据英国(en-GB)地区设置来解析消息,你将创建名为 format_en_GB.properties、exceptions_en_GB.properties 和windows_en_GB.properties 的文件。
通常情况下,地域性解析是由应用程序的周围环境管理的。在下面的例子中,(英国)信息所依据的地方语言是手动指定的:
# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
运行上述程序的结果输出如下:
Ebagum lad, the 'userDao' argument is required, I say, required.
你也可以使用 MessageSourceAware 接口来获取对任何已定义的 MessageSource 的引用。任何定义在实现 MessageSourceAware 接口的 ApplicationContext 中的 Bean,在 Bean 被创建和配置时都会被注入 ApplicationContext 的 MessageSource。
:::info 因为 Spring 的 MessageSource 是基于 Java 的 ResourceBundle,它不会合并具有相同基名的捆绑包,而是 只使用找到的第一个捆绑包。随后的具有相同基名的消息包会被忽略。 ::: :::info 作为 ResourceBundleMessageSource 的替代品,Spring 提供了一个 ReloadableResourceBundleMessageSource 类。这个变体支持相同的捆绑文件格式,但比基于 JDK 的标准 ResourceBundleMessageSource 实现更灵活。特别是,它允许从任何 Spring 资源位置(不仅仅是 classpath)读取文件,并支持捆绑属性文件的热重载(同时在两者之间有效地缓存它们)。详情见ReloadableResourceBundleMessageSource javadoc。 :::