该系列文档是本人在学习 Spring MVC 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读html
Spring 版本:5.2.4.RELEASEjava
该系列其余文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》git
LocaleResolver
组件,本地化(国际化)解析器,提供国际化支持github
先来回顾一下在 DispatcherServlet
中处理请求的过程当中哪里使用到 LocaleResolver
组件,能够回到《一个请求的旅行过程》中的 DispatcherServlet
的 processDispatchResult
方法中看看,以下:web
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { // ... 省略相关代码 // <3> 是否进行页面渲染 if (mv != null && !mv.wasCleared()) { // <3.1> 渲染页面 render(mv, request, response); // <3.2> 清理请求中的错误消息属性 // 由于上述的状况二中 processHandlerException 会经过 WebUtils 设置错误消息属性,因此这里得清理一下 if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } // ... 省略相关代码 } protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. // <1> 解析 request 中得到 Locale 对象,并设置到 response 中 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); // ... 省略相关代码 // 得到 View 对象 View view; String viewName = mv.getViewName(); // ... 省略相关代码 view = mv.getView(); // ... 省略相关代码 try { // <3> 设置响应的状态码 if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // <4> 渲染页面 view.render(mv.getModelInternal(), request, response); } // ... 省略相关代码 }
在执行完handler
处理器后,须要对返回的 ModelAndView 对象进行处理,可能须要调用 render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
方法,渲染页面spring
能够看到须要先经过 LocaleResolver 从请求中解析出 java.util.Locale
对象后端
org.springframework.web.servlet.LocaleResolver
,本地化(国际化)解析器,提供国际化支持,代码以下:app
public interface LocaleResolver { /** * 从请求中,解析出要使用的语言。例如,请求头的 "Accept-Language" */ Locale resolveLocale(HttpServletRequest request); /** * 设置请求所使用的语言 */ void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale); }
LocaleResolver 接口体系的结构以下:前后端分离
在 DispatcherServlet
的 initLocaleResolver(ApplicationContext context)
方法,初始化 LocaleResolver 组件,方法以下:ide
private void initLocaleResolver(ApplicationContext context) { try { // 从上下文中获取Bean名称为'localeResolver'的对象 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.localeResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.localeResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. /** * 从配置文件中获取默认的 {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver} */ this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isTraceEnabled()) { logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver.getClass().getSimpleName() + "]"); } } }
得到 Bean 名称为 "localeResolver",类型为 LocaleResolver 的 Bean ,将其设置为 localeResolver
若是未得到到,则得到默认配置的 LocaleResolver 实现类,调用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface)
方法,就是从 DispatcherServlet.properties
文件中读取 LocaleResolver 的默认实现类,以下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
我看了一下,Spring Boot 没有提供其余的实现类,默认也是这个
org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
,实现 LocaleResolver 接口,经过检验 HTTP 请求的Accept-Language
头部来解析区域,默认的实现类
public class AcceptHeaderLocaleResolver implements LocaleResolver { private final List<Locale> supportedLocales = new ArrayList<>(4); @Nullable private Locale defaultLocale; }
上面两个属性默认都没有设置值
实现 resolveLocale(HttpServletRequest request)
方法,从请求中解析出 java.util.Locale
对象,方法以下:
@Override public Locale resolveLocale(HttpServletRequest request) { // <1> 获取默认的语言环境 Locale defaultLocale = getDefaultLocale(); // <2> 若是请求头 'Accept-Language' 为空,且默认语言环境不为空,则返回默认的 if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } // <3> 从请求中获取 Locale 对象 `requestLocale` Locale requestLocale = request.getLocale(); // <4> 获取当前支持的 `supportedLocales` 集合 List<Locale> supportedLocales = getSupportedLocales(); // <5> 若是支持的 `supportedLocales` 集合为空,或者包含请求中的 `requestLocale` ,则返回请求中的语言环境 if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { return requestLocale; } // <6> 从请求中的 Locale 们和支持的 Locale 集合进行匹配 Locale supportedLocale = findSupportedLocale(request, supportedLocales); // <7> 若是匹配到了则直接返回匹配结果 if (supportedLocale != null) { return supportedLocale; } // <8> 默认的 `defaultLocale` 不为空则直接返回,不然返回请求中获取到的 `requestLocale` 对象 return (defaultLocale != null ? defaultLocale : requestLocale); }
获取默认的语言环境
若是请求头 Accept-Language
为空,且默认语言环境不为空,则返回默认对象 defaultLocale
从请求中获取 Locale 对象 requestLocale
调用 getSupportedLocales
方法,获取当前支持的 supportedLocales
集合,默认为空
若是支持的 supportedLocales
集合为空,或者包含请求中的 requestLocale
,则返回请求中的语言环境
调用 findSupportedLocale(HttpServletRequest request, List<Locale> supportedLocales)
方法,从请求中的 Locale 们和支持的 Locale 集合进行匹配,以下:
@Nullable private Locale findSupportedLocale(HttpServletRequest request, List<Locale> supportedLocales) { Enumeration<Locale> requestLocales = request.getLocales(); Locale languageMatch = null; while (requestLocales.hasMoreElements()) { Locale locale = requestLocales.nextElement(); if (supportedLocales.contains(locale)) { if (languageMatch == null || languageMatch.getLanguage().equals(locale.getLanguage())) { // Full match: language + country, possibly narrowed from earlier language-only match return locale; } } else if (languageMatch == null) { // Let's try to find a language-only match as a fallback for (Locale candidate : supportedLocales) { if (!StringUtils.hasLength(candidate.getCountry()) && candidate.getLanguage().equals(locale.getLanguage())) { languageMatch = candidate; break; } } } } return languageMatch; }
若是匹配到了则直接返回匹配结果
默认的 defaultLocale
不为空则直接返回,不然返回请求中获取到的 requestLocale
对象
默认状况下,supportedLocales
与defaultLocale
属性都是空的,因此 AcceptHeaderLocaleResolver 使用Accept-Language
请求头来构造 Locale 对象
例如请求的请求头中会有zh-CN,zh;q=0.9
数据,那么这里解析出来 Locale 对象就对应language="zh" region="CN"
数据
本文分析了 LocaleResolver
组件,本地化(国际化)解析器,提供国际化支持。笔者实际上没有接触过该组件,由于目前的项目大多数都已经先后端分离了,这里只是浅显的介绍了该接口,感兴趣的能够去 Google 一下😅 有点水~
参考文章:芋道源码《精尽 Spring MVC 源码分析》