首页 > 编程语言 > 详细

精尽Spring MVC源码分析 - LocaleResolver 组件

时间:2021-03-05 22:13:18      阅读:35      评论:0      收藏:0      [点我收藏+]

转:

精尽Spring MVC源码分析 - LocaleResolver 组件

该系列文档是本人在学习 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 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 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 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 supportedLocales) 方法,从请求中的 Locale 们和支持的 Locale 集合进行匹配,如下:

    @Nullable
    private Locale findSupportedLocale(HttpServletRequest request, List supportedLocales) {
        Enumeration 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 组件

精尽Spring MVC源码分析 - LocaleResolver 组件

原文:https://www.cnblogs.com/wangtcc/p/14487647.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!