文章
问答
冒泡
spring boot mvc 中处理国际化问题

在java工程中,经常使用ResourceBundle来处理国际化问题。

在spring中有一个ResourceBundleMessageSource 类,用来读取ResourceBundle中的内容。从源码可以看到,其实这里最主要的一个操作,就是获取Locale这个对象,里面会包括语言,国家的相关信息。而ResourceBundle根据Locale获取到对应的语言文件。

spring boot在启动的时候会创建ResourceBundleMessageSource的bean。我们可以通过设置
spring.messages.basename 的值来配置启动的时候会加载哪些对象到缓存里。

MessageSourceAutoConfiguration

@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
    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;
}


以入参校验为例
LocalValidatorFactoryBean

public void setValidationMessageSource(MessageSource messageSource) {
   this.messageInterpolator = HibernateValidatorDelegate.buildMessageInterpolator(messageSource);
}

private static class HibernateValidatorDelegate {

   public static MessageInterpolator buildMessageInterpolator(MessageSource messageSource) {
      return new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(messageSource));
   }
}


这里注入的对象就是一个ResourceBundleMessageSource

那么,在请求过程中,如何去判断当前的语言是什么呢?在接受到请求之后,就会有默认的过滤器去解析http requet header中的Accept-Language,然后选择当前的语言,并且赋值给LocaleContextHolder

RequestContextFilter

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
    this.initContextHolders(request, attributes);

    try {
        filterChain.doFilter(request, response);
    } finally {
        this.resetContextHolders();
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Cleared thread-bound request context: " + request);
        }

        attributes.requestCompleted();
    }

}

private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
    LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
    RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Bound request context to thread: " + request);
    }

}


HttpServletRequestImpl

@Override
public Locale getLocale() {
    return getLocales().nextElement();
}

@Override
public Enumeration<Locale> getLocales() {
    final List<String> acceptLanguage = exchange.getRequestHeaders().get(Headers.ACCEPT_LANGUAGE);
    List<Locale> ret = LocaleUtils.getLocalesFromHeader(acceptLanguage);
    if(ret.isEmpty()) {
        return new IteratorEnumeration<>(Collections.singletonList(Locale.getDefault()).iterator());
    }
    return new IteratorEnumeration<>(ret.iterator());
}


此时spring mvc已经根据hearder中的信息,为我们在LocaleContextHolder中填充了locale的信息。

在从ResourceBundleMessageSource获取对象的时候,我们就可以知道应该去拿对应的语言文件了。


关于作者

落雁沙
非典型码农
获得点赞
文章被阅读