最近正在把以前的一个Spring项目整改为基于Spring boot配置的项目。中间遇到一个国际化资源加载的问题,正常来说只要在application.properties文件中定义正确的资源文件路径,Spring boot就启动时就会自动加载资源。
spring.messages.basename=i18n/message
但是我的项目修改后获取消息时系统报错,找不到对应语言的资源配置。于是试图找到原因。Google好久都没找到,简直好像就我一个人遇到这鬼问题一样?。只好自己一步一步去调试。
调试中首先发现系统在调用MessageSource的地方注入的不是MessageSourceAutoConfiguration中定义的ResourceBundleMessageSource对象,而是一个DelegatingMessageSource对象,而且这个对象是空的什么都没有。MessageSourceAutoConfiguration中的定义如下:
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(this.basename)) {
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(this.basename)));
}
if (this.encoding != null) {
messageSource.setDefaultEncoding(this.encoding.name());
}
messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
messageSource.setCacheSeconds(this.cacheSeconds);
messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
return messageSource;
}
调试Spring boot 启动过程找到了 DelegatingMessageSource 对象来源, 在启动过程中如果Spring没有找到messageSource定义,就会自动创建一个 DelegatingMessageSource 对象提供给SpringContext。也就是说 MessageSourceAutoConfiguration 根本没有被加载。打断点在 MessageSourceAutoConfiguration 中也确定了Spring boot启动时根本没有执行 MessageSourceAutoConfiguration 的定义。
/**
* 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 + "]");
}
}
}
继续调试发现 MessageSourceAutoConfiguration 上有 @Conditional(ResourceBundleCondition.class) 这么个注解,@Conditional 官方介绍 Indicates that a component is only eligible for registration when all specified conditions match. 大概意思就是只有满足所有条件才会被注册。继续去看ResourceBundleCondition.class的实现。
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
String basename) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("ResourceBundle");
for (String name : StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) {
return ConditionOutcome
.match(message.found("bundle").items(resource));
}
}
}
return ConditionOutcome.noMatch(
message.didNotFind("bundle with basename " + basename).atAll());
}
这个方法是根据basename(上面配置的“i18n/message”)存不存在返回是否满足条件,也就是去找classpath:i18n/message.properties文件,看了下目录确实没有这个文件(只有message_**.properties)。因为之前项目messageSource加载时自己配置的,所以并没有这个问题。但现在Spring boot中没有找到message.properties这个默认文件整个messageSource就不加载了。。?简直思密达。。。??总算是解决这个奇葩问题。 话说真的就只有我遇到这问题么,没搜到没搜到啊。。。。?
最后总结Spring boot自动加载messageSource一定要有一个默认的资源文件。也就是basename.properties。好不科学啊,之前没有找到默认会默认取中文(没考虑过原理?),现在没有默认整个就不加载了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。