SpringBoot国际化spring.messages.basename没有生效问题

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

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

  最近在做基于Spring boot配置的项目。中间遇到一个国际化资源加载的问题,正常来说只要在application.properties文件中定义正确的资源文件路径,Spring boot就启动时就会自动加载资源。

1

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。好不科学啊,之前没有找到默认会默认取中文(没考虑过原理😆),现在没有默认整个就不加载了。

注:文章转自 Spring boot 国际化自动加载资源文件问题 - p1uto - 博客园 (cnblogs.com)

作者:地平线很远

出处:用户中心 - 博客园

### 解析 Spring Boot 国际化组件不生效的原因 当遇到国际化组件在 Spring Boot 中不生效的情况时,通常是因为消息源文件未被正确识别或配置不当。确保国际化功能正常工作需注意几个方面。 #### 配置 `messages.properties` 文件位置 Spring Boot 默认会在 classpath 下查找名为 `messages.properties` 的属性文件作为基础的语言包[^3]。如果自定义了其他名称的消息源文件,则需要通过 `spring.messages.basename` 属性指定其前缀名来让应用程序知道去哪里寻找这些资源文件。 ```yaml # application.yml example spring: messages: basename: i18n/messages # 自定义的基础名字,默认为classpath:/messages ``` #### 设置 LocaleResolver 和 MessageSource Bean 为了使多语言切换有效果,还需要注册相应的 `LocaleResolver` 来解析客户端请求中的区域设置信息,并创建一个 `MessageSource` bean 实现对不同地区特定的信息获取逻辑。 ```java @Configuration public class InternationalizationConfig { @Bean public LocaleResolver localeResolver() { SessionLocaleResolver slr = new SessionLocaleResolver(); slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return slr; } @Bean public ResourceBundleMessageSource messageSource(){ ResourceBundleMessageSource source=new ResourceBundleMessageSource(); source.setBasename("i18n/messages"); // 对应于resources下的目录结构 source.setDefaultEncoding("UTF-8"); return source; } } ``` #### 使用 Thymeleaf 进行页面渲染 对于前端展示部分,在 HTML 页面里可以通过 Thymeleaf 表达式语法访问已定义好的翻译词条: ```html <!-- index.html --> <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title th:text="#{title}">Page Title</title> <!-- #{key} 形式的表达式用于取值 --> <body> <p th:text="#{welcome.message}"></p> ... </body> </html> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值