该系列文档是本人在学习 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. jQuery 第二章 实例方法 DOM操作选择元素相关方法

    进一步选择元素相关方法:  .get() .eq() .find() .filter() .not() .is() .has() .add()集中操作  .end()回退操作 .get() $(&qu ...

  2. python3 Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接

    报错源代码from selenium import webdriverimport unittestimport timefrom HTMLTestRunner import HTMLTestRunn ...

  3. C++基础知识篇:C++ 变量类型

    变量其实只不过是程序可操作的存储区的名称.C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上. 变量的名称可以由字母.数字和下划线字 ...

  4. 一枚程序猿的MacBook M1详细体验报告

    前言 2020年11月11日双十一上午,苹果发布了M1芯片的新款Mac,其最大的变化就是将处理器从Intel换成了苹果自研的ARM芯片M1. 上一次苹果更换Mac芯片要追溯到2006年,14年前,苹果 ...

  5. 跟随杠精的视角一起来了解Redis的主从复制

    不想弹好吉他的撸铁狗,都不是好的程序猿 虽然说单机的Redis性能很好,也有完备的持久化机制,那如果你的业务体量真的很大,超过了单机能够承载的上限了怎么办?不做任何处理的话Redis挂了怎么办?带着这 ...

  6. CentOS下搭建文件共享服务

    nfs部署以及优化 Server端配置 安装rpm服务包 yum install -y nfs-utils 创建数据挂载点 mkdir -p /data 编辑exports文件 vi /etc/exp ...

  7. 我与oracle的爱恨情仇(第一章)

    关于配置 问题一 可能是虚拟机桥接问题 问题解决的链接:[如何更改虚拟机桥接](https://jingyan.baidu.com/article/9113f81b7cfbd22b3214c790.h ...

  8. 去除openwrite.cn博客验证码限制

    相信有的小伙伴肯定遇到过如下这种情况,但是作为老白嫖党肯定是 「下次一定」 了,所以今天我们来看看如何不关注公众号实现 「阅读原文」. 如何解决呢? 1.通过 F12 打开控制台,切换至 Elemen ...

  9. 5.3 Spring5源码--Spring AOP使用接口方式实现

    Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解. 本文重点介绍Spring使用接口方式实现AOP. 使用接口方式实现AOP以了解为目的. 更好地理解动态 ...

  10. moviepy音视频剪辑VideoClip类to_mask方法、to_RGB、afx方法

    ☞ ░ 前往老猿Python博文目录 ░ moviepy音视频剪辑VideoClip类to_mask方法和to_RGB方法用于进行剪辑内容变换. 1.to_mask方法 to_mask方法返回一个由调 ...