Spring boot 國際化自動加載資源文件問題
最近在做基於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。好不科學啊,之前沒有找到默認會默認取中文(沒考慮過原理😆),現在沒有默認整個就不加載了。
注:文章轉自 https://segmentfault.com/a/1190000010757338