`
234390216
  • 浏览: 10192369 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A5ee55b9-a463-3d09-9c78-0c0cf33198cd
Oracle基础
浏览量:460747
Ad26f909-6440-35a9-b4e9-9aea825bd38e
springMVC介绍
浏览量:1771676
Ce363057-ae4d-3ee1-bb46-e7b51a722a4b
Mybatis简介
浏览量:1395339
Bdeb91ad-cf8a-3fe9-942a-3710073b4000
Spring整合JMS
浏览量:393855
5cbbde67-7cd5-313c-95c2-4185389601e7
Ehcache简介
浏览量:678187
Cc1c0708-ccc2-3d20-ba47-d40e04440682
Cas简介
浏览量:529242
51592fc3-854c-34f4-9eff-cb82d993ab3a
Spring Securi...
浏览量:1178648
23e1c30e-ef8c-3702-aa3c-e83277ffca91
Spring基础知识
浏览量:461678
4af1c81c-eb9d-365f-b759-07685a32156e
Spring Aop介绍
浏览量:150089
2f926891-9e7a-3ce2-a074-3acb2aaf2584
JAXB简介
浏览量:66813
社区版块
存档分类
最新评论

Spring(21)——国际化MessageSource

阅读更多

21 国际化MessageSource

Spring中定义了一个MessageSource接口,以用于支持信息的国际化和包含参数的信息的替换。MessageSource接口的定义如下,对应的方法说明已经在方法上注释了。

public interface MessageSource {

	/**
	 * 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
	 * @param 需要进行解析的code,对应资源文件中的一个属性名
	 * @param 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
	 * @param defaultMessage 当对应code对应的信息不存在时需要返回的默认值
	 * @param locale 对应的Locale
	 * @return
	 */
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

	/**
	 * 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
	 * @param code 需要进行解析的code,对应资源文件中的一个属性名
	 * @param args 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
	 * @param locale 对应的Locale
	 * @return 
	 * @throws NoSuchMessageException 如果对应的code不能被解析则抛出该异常
	 */
	String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;

	/**
	 * 通过传递的MessageSourceResolvable对应来解析对应的信息
	 * @param resolvable 
	 * @param locale 对应的Locale
	 * @return 
	 * @throws NoSuchMessageException 如不能解析则抛出该异常
	 */
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

我们熟悉的ApplicationContext接口继承了MessageSource接口,所以我们所有的ApplicationContext实现类都实现了MessageSource接口,也就是我们我们可以通过ApplicationContext来调用MessageSource接口方法以实现信息的国际化和替换信息中包含的参数。所有ApplicationContext实现类对MessageSource接口的实现都是在AbstractApplicationContext中实现的,其对MessageSource接口实现的源码如下:

@Override
public String getMessage(String code, Object args[], String defaultMessage, Locale locale) {
	return getMessageSource().getMessage(code, args, defaultMessage, locale);
}

@Override
public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
	return getMessageSource().getMessage(code, args, locale);
}

@Override
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
	return getMessageSource().getMessage(resolvable, locale);
}

/**
 * Return the internal MessageSource used by the context.
 * @return the internal MessageSource (never {@code null})
 * @throws IllegalStateException if the context has not been initialized yet
 */
private MessageSource getMessageSource() throws IllegalStateException {
	if (this.messageSource == null) {
		throw new IllegalStateException("MessageSource not initialized - " +
				"call 'refresh' before accessing messages via the context: " + this);
	}
	return this.messageSource;
}

从中我们可以看到AbstractApplicationContext对MessageSource的实现都是自身所持有的MessageSource类型的messageSource对象来实现的。那么对应的messageSource又是如何初始化的呢?在其中定义了一个initMessageSource()来做对应的初始化工作,其源码如下。

/**
 * Initialize the MessageSource.
 * Use parent's if none defined in this context.
 */
protected void initMessageSource() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
		this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
		// Make MessageSource aware of parent MessageSource.
		if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
			HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
			if (hms.getParentMessageSource() == null) {
				// Only set parent context as parent MessageSource if no parent MessageSource
				// registered already.
				hms.setParentMessageSource(getInternalParentMessageSource());
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Using MessageSource [" + this.messageSource + "]");
		}
	}
	else {
		// Use empty MessageSource to be able to accept getMessage calls.
		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 + "]");
		}
	}
}

从上述源码中我们可以看到如果bean容器中存在一个名为messageSource(MESSAGE_SOURCE_BEAN_NAME常量对应的值为messageSource)的bean,则取该bean作为messageSource,如果对应的messageSource是一个HierarchicalMessageSource,则会在父容器存在的情况下取父容器对应的messageSource作为当前messageSource的parentMessageSource。如果当前bean容器中不存在beanName为messageSource的bean,则会生成一个DelegatingMessageSource来作为当前的MessageSource。DelegatingMessageSource基本算是对MessageSource的一个空的实现,在对应父容器的messageSource存在时就使用父容器的messageSource处理,否则就不处理,具体可以参考Spring的API文档或查看DelegatingMessageSource的源码。

鉴于ApplicationContext实现类对MessageSource接口实现的这种机制,如果我们需要通过ApplicationContext来获取国际化信息,那么我们只需要在对应的ApplicationContext中定义一个MessageSource类型的bean,并且指定对应的beanName为messageSource即可。Spring中对MessageSource提供了三个实现类,分别是ReloadableResourceBundleMessageSource、StaticMessageSource和ResourceBundleMessageSource。

21.1 ResourceBundleMessageSource

ResourceBundleMessageSource是基于JDK ResourceBundle的MessageSource接口实现类。它会将访问过的ResourceBundle缓存起来,以便于下次直接从缓存中获取进行使用。

21.1.1示例

如下我们在bean容器中定义了一个messageSource和一个名为hello的bean,并指定了messageSource的basename为message,即根资源文件应为类路径下的message.properties,其它的都是需要带Locale后缀的,如中国大陆是message_zh_CN.properties。

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basename" value="message"/>
</bean>

<bean id="hello" class="com.app.Hello"/>

其中类Hello的代码如下所示,我们可以看到其通过实现ApplicationContextAware接口注入了当前的ApplicationContext对象,然后在其doSomething()方法中我们通过注入的ApplicationContext对象以code为“appName”获取从对应的国际化信息,这里在中文环境下默认就会从message_zh_CN.properties文件中获取。

public class Hello implements ApplicationContextAware {

	private ApplicationContext context;
	
	public void doSomething() {
		String appName = context.getMessage("appName", null, null);
		System.out.println(appName);
	}
	
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.context = applicationContext;
	}

}

接下来我们在类路径下分别建立message_zh_CN.properties文件和message.properties文件。其内容分别对应如下。

  • 1、message.properties文件内容
appName=test
  • 2、message_zh_CN.properties文件内容
appName=测试

接下来我们运行如下测试代码,将看到控制台将输出“测试”,因为在获取对应信息时我们没有指定Locale,所以默认会取当前的Locale,即zh_CN,所以对应的信息将从message_zh_CN.properties文件中获取。

@ContextConfiguration({"classpath:/applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationContextTest {

	@Autowired
	private ApplicationContext context;
	
	@Test
	public void test() {
		Hello hello = context.getBean("hello", Hello.class);
		hello.doSomething();
	}
	
}

21.1.2 指定多个basename

在上述示例中我们通过setBasename()指定了ResourceBundleMessageSource的一个对应资源文件的基名称。但有时候我们可能会存在多种分类的资源文件,它们对应不同的基名称,如view.properties、remind.properties等,对于这种情况我们就可以通过ResourceBundleMessageSource的setBasenames()方法来指定多个basename。

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basenames">
		<array>
			<value>remind</value>
			<value>view</value>
		</array>
	</property>
</bean>

21.1.3 defaultEncoding

可以通过setDefaultEncoding()来指定将用来加载对应资源文件时使用的编码,默认为空,表示将使用默认的编码进行获取。

21.1.4 fallbackToSystemLocale

默认情况下,当指定Locale不存在某code对应的属性时,默认将尝试从系统对应的Locale中解析对应的code,只有都不能解析时才会使用基文件进行解析,如果还不能解析则将抛出异常。打个比方针对基名称“message”我们有两个属性文件, message.properties和message_zh_CN.properties,其中前者定义了appName=test,且定义了hello=hello,而后者只定义了appName=测试,那么当我们通过如下代码获取对应code对应的信息时,输出将如代码中注释所示,即输出两个“测试”,一个“hello”。

System.out.println(this.messageSource.getMessage("appName", null, null));//测试
System.out.println(this.messageSource.getMessage("appName", null, Locale.ENGLISH));//测试
System.out.println(this.messageSource.getMessage("hello", null, Locale.CHINA));//hello

第一个输出因为我们没有指定Locale,则默认会使用当前系统的Locale,即中文环境,此时将从message_zh_CN.properties中获取对应code对应的信息,所以输出为“测试”。第二个是因为我们没有定义Locale.ENGLISH对应的本地资源文件,默认将使用当前系统的Locale,即Locale.CHINA,所以输出为“测试”。第三个是因为Locale.CHINA对应的资源文件message_zh_CN.properties文件中不存在对应code的属性,所以将从基文件message.properties中获取,即输出为“hello”。

前面已经提到了当指定Locale不能解析指定的code时,默认将使用系统当前的Locale来解析对应的code,这是通过ResourceBundleMessageSource的fallbackToSystemLocale属性来定义的,默认为true,我们可以通过对应的set方法来设置其值为false,以使在指定的Locale不能解析指定的code时将使用基文件来解析对应的code。

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basename" value="message"/>
	<property name="fallbackToSystemLocale" value="false"/>
</bean>

当我们指定了fallbackToSystemLocale为false后,再运行上述测试代码时对应结果将如下。其中第一个跟第三个的输出结果将不变,第二个将变为“test”,因为此时使用Locale.ENGLISH不能解析appName时将使用基文件message.properties来解析。

System.out.println(this.messageSource.getMessage("appName", null, null));//测试
System.out.println(this.messageSource.getMessage("appName", null, Locale.ENGLISH));//test
System.out.println(this.messageSource.getMessage("hello", null, Locale.CHINA));//hello

关于ResourceBundleMessageSource的更多信息请参考Spring API文档,或查看对应的源码。

21.2 ReloadableResourceBundleMessageSource

ReloadableResourceBundleMessageSource是以ResourceBundleMessageSource结尾的,但实际上它跟ResourceBundleMessageSource没有什么直接的关系。ReloadableResourceBundleMessageSource也是对MessageSource的一种实现,其用法配置等和ResourceBundleMessageSource基本一致。所不同的是ReloadableResourceBundleMessageSource内部是使用PropertiesPersister来加载对应的文件,这包括properties文件和xml文件,然后使用java.util.Properties来保存对应的数据。

另外,ReloadableResourceBundleMessageSource允许我们指定非类路径下的文件作为对应的资源文件,而ResourceBundleMessageSource是限制了我们只能将对应的资源文件放置在类路径下的。在指定basename时,我们还可以使用Spring支持的资源文件的前缀,如classpath等。

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	<property name="basename" value="classpath:message"/>
	<property name="fallbackToSystemLocale" value="false"/>
	<property name="defaultEncoding" value="UTF-8"/>
</bean>

21.2.1 cacheSeconds

ReloadableResourceBundleMessageSource也是支持缓存对应的资源文件的,默认的缓存时间为永久,即获取了一次资源文件后就将其缓存起来,以后再也不重新去获取该文件。这个可以通过setCacheSeconds()方法来指定对应的缓存时间,单位为秒。前面我们已经说过ResourceBundleMessageSource也是会缓存对应的资源文件的,而且其也可以通过setCacheSeconds()方法指定对应的缓存时间,但是即使指定了也不会生效,其不会对缓存过的文件重新加载。

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	<property name="basename" value="classpath:message"/>
	<property name="cacheSeconds" value="1200"/>
</bean>

21.2.2 concurrentRefresh

ReloadableResourceBundleMessageSource使用concurrentRefresh属性来控制是否允许并发刷新,默认为true。其表示当线程A正在刷新缓存的资源文件F时,线程B也准备刷新缓存的资源文件F,那么线程A将继续执行刷新缓存的资源文件F的动作,而线程B将直接获取到原来缓存的资源文件F,当然这里也可以是取的线程A刚刚刷新的那个资源文件F。如果我们设定concurrentRefresh为false,那么先获取到对应锁的线程A将先刷新缓存中的资源文件F,然后在其释放对应的锁后,线程B将获取到对应的锁并再一次刷新缓存中的资源文件F。

21.3 注入MessageSource

除了直接使用ApplicationContext对象来获取对应code的国际化信息外,我们还可以给对应的bean直接注入一个MessageSource对象以直接通过对应的MessageSource对象来获取对应code的国际化信息。给bean注入MessageSource主要有两种方式,一种是直接注入,一种是间接的通过实现MessageSourceAware接口进行注入。

21.3.1 直接注入

直接注入就可以跟普通bean注入一样进行注入,可以使用注解标注进行注入,也可以使用XML配置进行注入。以下是一个使用XML方式通过set方法进行注入的示例。

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basename" value="message"/>
</bean>

<bean id="hello" class="com.app.Hello">
	<property name="messageSource" ref="messageSource"/>
</bean>

对应Hello的定义如下。

public class Hello {

	private MessageSource messageSource;
	
	public void doSomething() {
		String appName = this.messageSource.getMessage("appName", null, null);
		System.out.println(appName);
	}
	
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}

}

21.3.2 实现MessageSourceAware接口

当一个bean实现了MessageSourceAware接口时,ApplicationContext在实例化对应的bean后会将自己作为MessageSource回调MessageSourceAware实现类的setMessageSource()方法以实现MessageSource的注入。如下代码中Hello类就实现了MessageSourceAware接口。

public class Hello implements MessageSourceAware {

	private MessageSource messageSource;
	
	public void doSomething() {
		String appName = this.messageSource.getMessage("appName", null, null);
		System.out.println(appName);
	}
	
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}

}

(注:本文是基于Spring4.1.0所写)

 

0
0
分享到:
评论
1 楼 Caelebs 2017-09-14  
  

相关推荐

    Spring2.5的国际化配置

    Spring2.5的国际化配置 基于浏览器语言的国际化配置 使用Spring的MVC,并且配置中有配置Resource文件 &lt;!-- 资源文件绑定器 --&gt; &lt;bean id="messageSource" class="org.springframework.context.support....

    Spring国际化

    Spring和其它的框架一样,也提供了国际化功能,它是通过MessageSource接口来实现的 ApplicationContext接口继承了MessageSource 。 MessageSource接口方法

    spring messageSource结合ehcache demo

    spring messageSource功能结合ehcache实现提示语句从数据库读取demo,注:sql脚本都在代码中,如有问题及时沟通指点。

    解决Spring国际化文案占位符失效问题的方法

    本篇文章主要介绍了解决Spring国际化文案占位符失效问题的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Spring.3.x企业应用开发实战(完整版).part2

     《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.8.1. 利用MessageSource实现国际化 3.8.2. 事件 3.8.3. 底层资源的访问 3.8.4. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.9.1. 使用Singleton-helper类 4. 资源 4.1. 简介 4.2. ...

    Spring 2.0 开发参考手册

    3.8.1. 利用MessageSource实现国际化 3.8.2. 事件 3.8.3. 底层资源的访问 3.8.4. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.9.1. 使用Singleton-helper类 4. 资源 4.1. 简介 ...

    springMVC实现国际化

    看了好多资料后整理的springMVC国际化,内容为亲测可用的eclipse项目,直接import即可。 页面点击后可以切换中文和英文两种国际化信息,可以再追加其他语言。

    Spring3.x企业应用开发实战(完整版) part1

     《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...

    Spring中文帮助文档

    3.8.2. 利用MessageSource实现国际化 3.8.3. 事件 3.8.4. 底层资源的访问 3.8.5. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.10. 以J2EE RAR文件的形式部署Spring ...

    spring chm文档

    3.8.1. 利用MessageSource实现国际化 3.8.2. 事件 3.8.3. 底层资源的访问 3.8.4. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.9.1. 使用Singleton-helper类 4. 资源 4.1. 简介 ...

    Spring API

    3.8.2. 利用MessageSource实现国际化 3.8.3. 事件 3.8.4. 底层资源的访问 3.8.5. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.10. 以J2EE RAR文件的形式部署Spring ...

    spring-proj-template:如何配置Spring项目的参考-Maven,Spring,MVC,Hibernate,EJB,DI,国际化

    基于数据库MessageSource的国际化 依赖注入 EJB启动Bean 没有测试,可耻的是我:) 注意:此示例仅包含基于xml的配置。 是相同项目模板的链接,但具有基于注释的配置。 要运行此应用程序,请安装Jboss AS 7.1.1,并...

    spring-proj-template-no-xml:如何通过注解配置 Spring 项目的参考 - maven、spring、mvc、hibernate、ejb、di、国际化

    Spring 项目模板(基于注解) 此项目模板与完全相同,但使用注释代替 XML ...基于数据库MessageSource的国际化 依赖注入 EJB 启动 Bean 要运行此应用程序,请安装 Jboss AS 7.1.1 并在必要时修改 deploy-script.bat。

    boot-login-service:基于SpringBoot、SpringSecurity的登录功能。包含验证码、踢出用户、国际化、全局异常、

    BootLoginService 本项目为基于Spring-Boot、Spring-Security的登录功能 作者邮箱: Spring Boot 用户登录功能实现session共享,踢出用户,锁定用户等。

    spring-mvc-params:Spring RequestHandler方法参数注入

    例子@Controllerpublic class AutowiredParamController {/** * Handles A requests * @param param an example url parameter * @param messageSource the MessageSource from the application context, * injected...

    Grails开源框架 - 使用指南

    不用重新启动服务器就可以进行重新加载利用内置的Spring 容器实现依赖注入基于Spring的MessageSource核心概念,提供了对国际化(i18n)的支持基于Spring事务抽象概念,实现事务服务层  借助于功能强大的Groovy动态...

    SPRING API 2.0.CHM

    MessageSource MessageSourceAccessor MessageSourceAware MessageSourceResolvable MessageSourceResourceBundle MessageTag MetaDataAccessException MetadataAwareAspectInstanceFactory ...

    使用Grails快速开发Web应用.rar

    不用重新启动服务器就可以进行重新加载利用内置的Spring 容器实现依赖注入基于Spring的MessageSource核心概念,提供了对国际化(i18n)的支持基于Spring事务抽象概念,实现事务服务层  借助于功能强大的Groovy动态...

    spring-framework-reference-4.1.2

    21 4. The IoC container ........................................................................................................ 22 4.1. Introduction to the Spring IoC container and beans ..............

Global site tag (gtag.js) - Google Analytics