该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.2.4.RELEASE

该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》

LocaleResolver 组件

LocaleResolver 组件,本地化(国际化)解析器,提供国际化支持

回顾

先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 LocaleResolver 组件,可以回到《一个请求的旅行过程》中的 DispatcherServletprocessDispatchResult 方法中看看,如下:

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) 方法,渲染页面

可以看到需要先通过 LocaleResolver 从请求中解析出 java.util.Locale 对象

LocaleResolver 接口

org.springframework.web.servlet.LocaleResolver,本地化(国际化)解析器,提供国际化支持,代码如下:

public interface LocaleResolver {
/**
* 从请求中,解析出要使用的语言。例如,请求头的 "Accept-Language"
*/
Locale resolveLocale(HttpServletRequest request); /**
* 设置请求所使用的语言
*/
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}

LocaleResolver 接口体系的结构如下:

初始化过程

DispatcherServletinitLocaleResolver(ApplicationContext context) 方法,初始化 LocaleResolver 组件,方法如下:

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() + "]");
}
}
}
  1. 获得 Bean 名称为 "localeResolver",类型为 LocaleResolver 的 Bean ,将其设置为 localeResolver

  2. 如果未获得到,则获得默认配置的 LocaleResolver 实现类,调用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是从 DispatcherServlet.properties 文件中读取 LocaleResolver 的默认实现类,如下:

    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

我看了一下,Spring Boot 没有提供其他的实现类,默认也是这个

AcceptHeaderLocaleResolver

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

实现 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);
}
  1. 获取默认的语言环境

  2. 如果请求头 Accept-Language 为空,且默认语言环境不为空,则返回默认对象 defaultLocale

  3. 从请求中获取 Locale 对象 requestLocale

  4. 调用 getSupportedLocales 方法,获取当前支持的 supportedLocales 集合,默认为空

  5. 如果支持的 supportedLocales 集合为空,或者包含请求中的 requestLocale ,则返回请求中的语言环境

  6. 调用 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;
    }
  7. 如果匹配到了则直接返回匹配结果

  8. 默认的 defaultLocale 不为空则直接返回,否则返回请求中获取到的 requestLocale 对象

默认情况下,supportedLocalesdefaultLocale 属性都是空的,所以 AcceptHeaderLocaleResolver 使用Accept-Language 请求头来构造 Locale 对象

例如请求的请求头中会有zh-CN,zh;q=0.9数据,那么这里解析出来 Locale 对象就对应language="zh" region="CN"数据

总结

本文分析了 LocaleResolver 组件,本地化(国际化)解析器,提供国际化支持。笔者实际上没有接触过该组件,因为目前的项目大多数都已经前后端分离了,这里只是浅显的介绍了该接口,感兴趣的可以去 Google 一下 有点水~

参考文章:芋道源码《精尽 Spring MVC 源码分析》

精尽Spring MVC源码分析 - LocaleResolver 组件的更多相关文章

  1. 精尽Spring MVC源码分析 - ViewResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  2. 精尽Spring MVC源码分析 - MultipartResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  6. 精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  7. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  9. 精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

随机推荐

  1. Pytest自动化测试 - 简易教程

    简介 pytest是动态编程语言Python专用的测试框架,它具有易于上手.功能强大.可扩展性好.兼容性强.效率高.第三方插件丰富等特点. 功能特征: 完整的文档,包括安装,教程和PDF文档 简单而又 ...

  2. 15.java设计模式之访问者模式

    基本需求: 电脑需要键盘鼠标等固定的组件组成 现在分为个人,组织等去买电脑,而同一种组件对不同的人(访问者)做出不同的折扣,从而电脑的价格也不一样 传统的解决方法:在组件内部进行判断访问人的类型,从而 ...

  3. 他是 ISIJ 第四名,也是在线知名题库的洛谷“网红”

    转载自加藤惠. 2020年国际初中生信息学竞赛(ISIJ)上,以优秀成绩拿下第四名年仅初三的张湫阳,成为最夺目的选手之一. 而且虽然是初三的选手,但他取得优异成绩后,不少网友并不感到陌生,纷纷留言: ...

  4. Java蓝桥杯——排列组合

    排列组合介绍 排列,就是指从给定n个数的元素中取出指定m个数的元素,进行排序. 组合,则是指从给定n个数的元素中仅仅取出指定m个数的元素,不考虑排序. 全排列(permutation) 以数字为例,全 ...

  5. Java蓝桥杯——递归练习题:走台阶(偶数版)

    递归练习题:走台阶(偶数版) 小明刚刚看完电影<第39级台阶>.离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级! 站在台阶前,他突然又想着一个问题: 如果我每一步只能迈上1个或2个 ...

  6. Java集合【6】-- Collection和Collections的区别

    刚开始学java的时候,分不清Collection和Collections,其实这两个东西是完全不一样的东西. Collection是一个接口,是java集合中的顶级接口之一,衍生出了java集合的庞 ...

  7. 【译】用 React 和 D3 创建图表

    本文翻译自:https://dzone.com/articles/charts-with-modern-react-and-d3 本文将介绍如何利用 D3JS 和 ReactJS 来创建基础图表. R ...

  8. [题解] 洛谷 P3393 逃离僵尸岛

    题目TP门 很明显是一个最短路,但是如何建图才是关键. 对于每一个不可遍历到的点,可以向外扩散,找到危险城市. 若是对于每一个这样的城市进行搜索,时间复杂度就为\(O(n^2)\),显然过不了.不妨把 ...

  9. partial conv

    Image Inpainting for Irregular Holes Using Partial Convolutions pytorch代码 论文贡献: 提出了部分卷积(partial conv ...

  10. python:列表的去重:两种方法的问题是:结果是没有保持原来的顺序。

    列表的去重 1.使用set的特型,python的set和其他语言类似, 是一个无序不重复元素集 orgList = [1,0,3,7,7,5] #list()方法是把字符串str或元组转成数组 for ...