Spring boot 国际化自动加载资源文件问题

Posted 地平线很远

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot 国际化自动加载资源文件问题相关的知识,希望对你有一定的参考价值。

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

 

以上是关于Spring boot 国际化自动加载资源文件问题的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 静态资源无法加载

Spring boot 静态资源无法加载

Spring boot国际化

《Spring揭秘》——IOC梳理3(资源加载,国际化)

spring boot + thymeleaf 3 国际化

使用 Spring Boot 和 Thymeleaf 加载静态资源