spring boot 2.0.5 + thymeleaf3 国际化,无法自动加载MessageSource,找不资源文件,页面访问出现问号

首先,需要明确一点,国际化资源文件必须包含默认文件和国家语言文件,且必须:

(1)直接放在类路径下

(2)或放在resource目录下

1、spring boot 添加 thymele3依赖

        <!-- 导入 thymeleaf 模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

2、添加国际化资源文件(我是放在了 resources目录)

login_zh_CN.properties:

login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名

3、在 application.properties 或 application.yml 文件中添加 basename 引用路径(很重要

#国际化文件
spring.messages.basename=i18n/login

4、通过 #{} 引用资源文件中的值

<form class="form-signin" action="dashboard.html">
			<img class="mb-4" th:src="@{asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
			<label class="sr-only" th:text="#{login.username}">Username</label>
			<input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only" th:text="#{login.password}">Password</label>
			<input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm">中文</a>
			<a class="btn btn-sm">English</a>
</form>

5、开始测试,发现界面上找不到资源文件中的值,一直出现??login_zh_CN??这种问题。遇到这种问题简直要疯了,于是各种baidu、google,找了好多文章,各种方案,都没有解决问题。静下心来,自己一步一步去调试。

      (1)自动加载配置类 MessageSourceAutoConfiguration,自动注入MessageResource,调试中发现,自动注入的不是ResourceBundleMessageSource 对象,而是DelegatingMessageSource对象,且其中没有值。在注入 MessageResource 的代码中打断点,发现确实没有运行,即没有自动注入ResourceBundleMessageSource。

        @Bean
	public MessageSource messageSource() {
		MessageSourceProperties properties = messageSourceProperties();
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(properties.getBasename())) {
			messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
					StringUtils.trimAllWhitespace(properties.getBasename())));
		}
		if (properties.getEncoding() != null) {
			messageSource.setDefaultEncoding(properties.getEncoding().name());
		}
		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
		Duration cacheDuration = properties.getCacheDuration();
		if (cacheDuration != null) {
			messageSource.setCacheMillis(cacheDuration.toMillis());
		}
		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
		return messageSource;
	}

       (2)在Spring boot 启动过程找到了 DelegatingMessageSource 对象来源, 在启动过程中如果Spring没有找到messageSource定义,就会自动创建一个 DelegatingMessageSource 对象提供给SpringContext。也就是说 MessageSourceAutoConfiguration 根本没有被加载。

    protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("messageSource")) {
            this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    hms.setParentMessageSource(this.getInternalParentMessageSource());
                }
            }

            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Using MessageSource [" + this.messageSource + "]");
            }
        } else {
            DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(this.getInternalParentMessageSource());
            this.messageSource = dms;
            beanFactory.registerSingleton("messageSource", this.messageSource);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Unable to locate MessageSource with name 'messageSource': using default [" + this.messageSource + "]");
            }
        }

    }

(3)到这里,就可以知道,为什么页面上没有找到资源了,因为 ResourceBundleMessageSource 对象没有注入容器中,找不到MessageResource 对象,返回了一个空的 DelegatingMessageSource 对象注入到了容器中。现在分析为什么配置类MessageSourceAutoConfiguration 中的 MessageResource 对象没有自动注入呢,查看 MessageSourceAutoConfiguration 类上的注解,可以看到 有如下一个注解:

@Conditional(ResourceBundleCondition.class)

@conditional 是spring 的条件判定注解,大致意思为:满足一定条件后,ResourceBundleCondition 类会注册生效。继续分析ResourceBundleCondition实现,这个类是一个内部类,经过分析,发现有如下判定是否生效的方法:

                @Override
		public ConditionOutcome getMatchOutcome(ConditionContext context,
				AnnotatedTypeMetadata metadata) {
			String basename = context.getEnvironment()
					.getProperty("spring.messages.basename", "messages");
			ConditionOutcome outcome = cache.get(basename);
			if (outcome == null) {
				outcome = getMatchOutcomeForBasename(context, basename);
				cache.put(basename, outcome);
			}
			return outcome;
		}

		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是否存在,来判定是否满足注册条件。basename就是我们前面配置的”i18n/login“(没有配置,默认为 类路径下的messages),根据basename去项目的类路径下寻找 i18n/login.properties ,若存在,会正常返回一个Resouce对象,满足注册条件,会成功加载 MessageResource 对象;若不存在,不满足条件,无法继续注册。

      这里说下我的问题,我是因为多建了一个resources文件夹导致,无法找到资源文件。到这里,终于把这个奇葩的问题解决了,记录一下。

总结:Spring boot 国际化配置时,一定要建立默认的 basename.properties 文件。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值