Spring Boot中实现可重载的MessageSource简明教程

原创 2017年09月28日 10:56:37

引言: 在Spring Boot中messages中定义的信息,如果发生变更,则需要重启应用。那该如何实现才可以不重启应用的情况下替换messages中的展示信息呢?本文将给出一个简要的教程。

1. MessageSoure的资源配置与读取

在之前的文章中,已经介绍过了如何在Spring Boot中进行资源的配置和读取以及相应的测试代码,感兴趣的读者,可以参照Spring Boot中支持i18n简明教程

2 可重载的MessageSource

在Spring中定义了ReloadableResourceBundleMessageSource类,提供可自动刷新的Messages更新,即用户在无需重载应用的前提下,可以自动更新线上系统的messages展示信息。
在其API文档中,其描述到如下信息:

In contrast to the JDK-based ResourceBundleMessageSource, this class uses Properties instances as its custom data structure for messages, loading them via a PropertiesPersister strategy from Spring Resource handles. This strategy is not only capable of reloading files based on timestamp changes, but also of loading properties files with a specific character encoding. It will detect XML property files as well.

翻译为中文的信息如下:

与JDK中的ResourceBundleMessageBundle相比,这个类使用Properties实例作为自定义的message存储结构,通过PropertiesPersister策略从Spring中Resource加载messages,同时也支持基于特定字符编码加载messages信息。 同时他也将检查xml的属性文件。

ReloadableResourceBundleMessageSource从命名上也可以感知到其余我们默认使用的MessageSource实例对象,都是实现了同一个接口MessageSource,所以他们的使用接口和调用方式是一致的。

3 覆盖Override实例

根据网络上的教程,首先读取spring.messages的属性信息:

@Value(“${spring.messages.basename}”)
private String basename;

@Value(“${spring.messages.cache-seconds}”)
private long cacheMillis;

@Value(“${spring.messages.encoding}”)
private String encoding;

这里只使用了3个属性,分别对应application.properties的属性信息,在初始化自定义的MessageSource实例中进行设置。
然后覆盖Spring Boot默认的对象实例需要基于@Bean来声明某个对象实例:

@Bean
public MessageSource initMessageSource() {
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();

log.info(“baseName====>:” + this.basename);
messageSource.setBasename(basename);
messageSource.setDefaultEncoding(encoding);
messageSource.setCacheMillis(cacheMillis);

String msg = messageSource.getMessage(“login.failure.msg”, null, Locale.CHINA);
log.info(“Msg====>” + msg);

return messageSource;
}

之前的测试代码无需修改;但是在测试中一直在提示以下异常:

0:32:33.548 INFO org.jd.test.controller.TestController.testCode@42 - Locale:zh
10:32:33.561 ERROR org.jd.test.controller.GlobalExceptionHandler.handleException@20 - Exception Msg:No message found under code ‘login.failure.msg’ for locale ‘zh_CN’.
org.springframework.context.NoSuchMessageException: No message found under code ‘login.failure.msg’ for locale ‘zh_CN’.
at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:69)
at org.jd.test.controller.TestController.testCode(TestController.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)

从异常信息可以感知到,在Messages中并未找到对应的message,但是messages的资源文件并未变动,那问题出在哪里呢?
经过分析,大概率应该是自定义的MessageSource实例已经被覆盖,但是我们在使用调用之时,并未获取争取的对象实例:

@Autowired
private MessageSource messageSource;
那该如何解决呢?

4 修复无法获取正确实例的问题

4.1 设置实例对象获取顺序

@Primary告诉Spring容器在基于同种类型加载实例之时,优先加载基于@Primary的对象实例。
于是,我们声明覆盖对象的代码就变更为:

@Primary
@Bean
public MessageSource initMessageSource() {
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();

log.info(“baseName====>:” + this.basename);
messageSource.setBasename(basename);
messageSource.setDefaultEncoding(encoding);
messageSource.setCacheMillis(cacheMillis);

String msg = messageSource.getMessage(“login.failure.msg”, null, Locale.CHINA);
log.info(“Msg====>” + msg);

return messageSource;
}
测试之后,可以正常获取message信息。

4.2 基于实例的名称指定加载

另外,我们还可以基于@Bean指定名称,然后在@Autowired加载实例之时,基于实例名称来加载相应的实例。
声明对象的代码在@Bean中增加一个命名:

@Bean(“myMessageSource”)
public MessageSource initMessageSource() {
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();

log.info(“baseName====>:” + this.basename);
messageSource.setBasename(basename);
messageSource.setDefaultEncoding(encoding);
messageSource.setCacheMillis(cacheMillis);

String msg = messageSource.getMessage(“login.failure.msg”, null, Locale.CHINA);
log.info(“Msg====>” + msg);

return messageSource;
}

在加载MessageSource之时,需要基于@Qualifier指定对象命名:

@Qualifier(value=”myMessageSource”)
@Autowired
private MessageSource messageSource;

然后经过测试,功能正确。

5 总结

Spring Boot提供非常简单易用的扩展机制,但是在自定义扩展之时,需要格外注意是否加载所期望的实例,这个需要进行通过@Primary或者@Bean的命名机制来指定解决冲突的问题。

相关文章推荐

spring的学习历程之利用MessageSource实现国际化占位符直接输出原因(一)

利用MessageSource实现国际化其中有一个着眼于传递参数给查找的消息,这些参数将被转换为字符串并插入到已查找到的消息中的占位符(译注:资源文件中花括号里的数字即为占位符)。#其中'{0}'会直...

spring中MessageSource实现国际化i18n

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

Spring 利用MessageSource实现国际化

Spring 利用MessageSource实现国际化 spring中ResourceBundleMessageSource与ReloadableResourceBundleMessag...
  • jiana227
  • jiana227
  • 2014年04月09日 10:28
  • 13654

Spring Boot系列教程五:使用properties配置文件实现多环境配置

实际项目开发过程中会用到多个环境,比如dev,test,product环境,不同的环境可能使用不同参数,为便于部署提高效率,本篇主要通过properties配置文件来实现多环境的配置。...

spring messageSource结合ehcache demo

  • 2017年07月06日 15:56
  • 23KB
  • 下载

Spring Boot系列教程三:使用devtools实现热部署

Eclipse下使用spring-tool-suite插件创建一个spring boot 工程,通过右键“Run As”--->"Spring Boot App"来启动工程,这时当我们对工程的内容进行...

Spring Web Service 简明教程

通过学习Spring WebServices Peoject里的Get started文档,能够学习到大部分sws(spring web service)的使用。 http://docs.sprin...

spring中MessageSource的配置使用方法--ResourceBundleMessageSource

spring中MessageSource的配置使用方法--ResourceBundleMessageSource ApplicationContext接口扩展了MessageSo...

spring源码学习笔记-初始化(五)-MessageSource/事件监听器

转自http://www.sandzhang.com/blog/2011/04/07/spring-study-notes-initialization-5/ refresh()方法中在上篇看完...

spring中MessageSource的配置使用方法1

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法:   String getMessage(String code, ...
  • qyf_5445
  • qyf_5445
  • 2012年10月29日 14:52
  • 19797
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring Boot中实现可重载的MessageSource简明教程
举报原因:
原因补充:

(最多只允许输入30个字)