地平线很远

     改变,就是好事
< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

统计

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

注:文章转自  https://segmentfault.com/a/1190000010757338

 

posted on   p1uto  阅读(5120)  评论(0编辑  收藏  举报

编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析
点击右上角即可分享
微信分享提示