Spring MVC 源码- LocaleResolver 组件

news2024/10/2 1:39:47

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

  1. 如果未获得到,则获得默认配置的 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. 获取默认的语言环境

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

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

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

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

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

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

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

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

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/369995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】json数据处理

Json是一种轻量级的数据交换格式。 文章目录1. cJson介绍2. 解析json数据3. 封装json数据4. 从文件中读取json1. cJson介绍 JSON对象是一个无序的"名称/值"键值对的集合&#xff1a; 以"{“开始&#xff0c;以”}"结束&#xff0c;允许嵌套使用&#xff…

相约3.8!罗姆EEPROM在线研讨会

科技的迭代更新速度不断超乎想象&#xff0c;人们也越来越追求数据的可追溯性和安全性&#xff0c;为避免意外情况导致数据丢失&#xff0c;在车载、工业等领域中&#xff0c;数据存储更经常使用安全性较好的EEPROM【带电可擦除可编程只读存储器】。与FLASH存储器的按“片”擦写…

偏向锁、轻量级所、自旋锁、重量级锁,它们都是什么?它们之间有什么关系?为什么会有这些锁?

互斥锁的本质是共享资源。 当有多个线程同时对一个资源进行操作时&#xff0c;为了线程安全&#xff0c;要对资源加锁。 更多基础内容参看上文《深入了解Java线程锁(一)》 接下来&#xff0c;我们来看看两个线程抢占重量级锁的情形&#xff1a; 上图讲述了两个线程ThreadA和…

JDBC-

文章目录JDBC1&#xff0c;JDBC概述1.1 JDBC概念1.2 JDBC本质1.3 JDBC好处2&#xff0c;JDBC快速入门2.1 编写代码步骤2.2 具体操作3&#xff0c;JDBC API详解3.1 DriverManager3.2 Connection &#xff08;事务归我管&#xff09;3.2.1 获取执行对象3.2.2 事务管理3.3 Stateme…

CSS 浮动【快速掌握知识点】

目录 前言 一、设置浮动属性 二、确定浮动元素的宽度 三、清除浮动 总结&#xff1a; 前言 CSS浮动是一种布局技术&#xff0c;它允许元素浮动到其父元素的左侧或右侧&#xff0c;从而腾出空间给其他元素。 一、设置浮动属性 使用CSS float属性将元素设置为浮动。例如&…

【华为OD机试模拟题】用 C++ 实现 - 数组的中心位置(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

MySQL —— 表的约束

文章目录1. null 空属性2. default 默认值3. comment 列描述4. zerofill 格式化输出5. primary key 主键6. auto_increment 自增长7. 唯一键8. unique key 外键前言&#xff1a; 表的约束主要是靠数据类型。有些情况&#xff0c;光靠数据类型约束是不够的&#xff0c;比如想要限…

【Java】ThreadLocal原理

​ ThreadLocal ThreadLocal意为线程本地变量&#xff0c;用于解决多线程并发时访问共享变量的问题。 每个线程都会有属于自己的本地内存&#xff0c;在堆&#xff08;也就是上图的主内存&#xff09;中的变量在被线程使用的时候会被复制一个副本线程的本地内存中&#xff0c…

【H5 | CSS | JS】如何实现网页打字机效果?快收下这份超详细指南(附源码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

在C#中初测OpencvSharp4

一、配置OpenCV 首先&#xff0c;我们新建一个工程&#xff0c;然后就是给这个工程配置OpenCV了&#xff0c;最简单的方法还是Nuget&#xff0c;来我们右键一个Nuget&#xff1a; 打开Nuget后&#xff0c;你可以直接输入OpenCVSharp4来查找&#xff0c;当然&#xff0c;如果你…

公司新来的00后真是卷王,工作没两年,跳槽到我们公司起薪20K都快接近我了

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

罗永浩进场之后,苹果入局之前:XR又寒冬了吗?

科技圈的悲欢并不相通。ChatGPT狂飙之际&#xff0c;XR领域正在迎来至暗时刻。岁末年初&#xff0c;就在罗永浩重返高科技创业,计划进军XR&#xff08;扩展现实&#xff09;类领域的时间段前后&#xff0c;接连出现了押注元宇宙的Meta裁员&#xff0c;Meta旗下VR工作室Ready At…

【华为OD机试模拟题】用 C++ 实现 - 快递业务站(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

【华为OD机试模拟题】用 C++ 实现 - 流水线(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 分积木(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 吃火锅(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - RSA 加密算法(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 构成的正方形数量(2023.Q1) 【华为OD机试模拟…

数据库|(六)连接查询

&#xff08;六&#xff09;连接查询1. 笛卡尔乘积2. 连接查询分类2.1 按年代分2.2 按功能分3. 等值连接(sql 92标准)3.1 特点3.2 一般使用3.3 为表取别名3.4 两表顺序可以调换3.5 可以加筛选3.6 可以加分组3.7 可以加排序3.8 可以实现三表连接4. 非等值连接(sql 92标准)5. sql…

【深度学习】GPT系列模型:语言理解能力的革新

GPT-1&#x1f3e1; 自然语言理解包括一系列不同的任务&#xff0c;例如文本蕴涵、问答、语义相似度评估和文档分类。尽管大量的未标记文本语料库很充足&#xff0c;但用于学习这些特定任务的标记数据却很稀缺&#xff0c;使得判别式训练模型难以达到良好的表现。我们证明&…

Spring(入门)

1. 什么是spring&#xff0c;它能够做什么?2. 什么是控制反转(或依赖注入)3. AOP的关键概念4. 示例 4.1 创建工程4.2 pom文件4.3 spring配置文件4.4 示例代码 4.4.1 示例14.4.2 示例2 &#xff08;abstract&#xff0c;parent示例&#xff09;4.4.3 使用有参数构造方法创建jav…

【华为OD机试模拟题】用 C++ 实现 - 找数字(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

显示器管理工具:BetterDisplay Pro Mac

BetterDisplay Pro Mac 是一个非常棒的工具&#xff01;它可以让您将显示器转换为完全可缩放的屏幕&#xff0c;允许亮度控制&#xff0c;提供 XDR/HDR 升级&#xff08;在兼容显示器上超过 100% 的额外亮度&#xff09;&#xff0c;完全调光为黑色&#xff0c;帮助您为 Mac 创…

PRML笔记3-绪论中最小化错误分类率的理解

这个小节的内容很少&#xff0c;因为自己数学水平实在太差&#xff0c;所以理解不到之处还请批评指正。 在分类任务中我们希望尽可能减少错误的分类&#xff0c;例如我们有一些病人的临床数据&#xff0c;希望通过这些临床数据对患者的诊断提供帮助&#xff0c;比如根据临床数据…