1.这篇文章让你搞懂 SpringMVC 国际化!
这篇文章让你搞懂 SpringMVC 国际化!
松哥之前写过 Spring Boot 国际化的问题,不过那一次没讲源码,这次咱们整点源码来深入理解下这个问题。
国际化,国检溯源码燕窝也叫 in,为啥叫这个名字呢?因为国际化英文是 internationalization ,在 i 和 n 之间有 个字母,所以叫 in。我们的应用如果做了国际化就可以在不同的语言环境下,方便的进行切换,最常见的就是中文和英文之间的切换,国际化这个功能也是相当的常见。
1. SpringMVC 国际化配置
先来说说用法,再来说源码,这样大家不容易犯迷糊。黄金阶梯指标源码我们先说在 SSM 中如何处理国际化问题。
首先国际化我们可能有两种需求:一种是通过请求头来判断当前的语言环境,另一种是通过请求参数来传递语言环境。接下来松哥通过一个简单的用法来和大家演示下具体玩法。
在项目的 resources 目录下新建语言文件,分别对应英中文环境,配置文件写好之后,还需要在 SpringMVC 容器中提供一个 ResourceBundleMessageSource 实例去加载这两个实例,然后在控制器中返回 login 视图即可。配置完成后,启动项目进行测试。
默认情况下,系统是根据请求头的中 Accept-Language 字段来判断当前的语言环境的。为了测试方便,可以使用 POSTMAN 进行测试,然后手动设置 Accept_Language 字段。java 优先队列源码测试中文和英文环境,都没问题,完美!同时观察 IDEA 控制台,也能正确打印出语言文字。
2. Spring Boot 国际化配置
Spring Boot 和 Spring 一脉相承,对于国际化的支持,默认是通过 AcceptHeaderLocaleResolver 解析器来完成的。所以在 Spring Boot 中做国际化,这一块我们可以不用配置,直接就开搞。
创建一个普通的 Spring Boot 项目,添加 web 依赖即可。配置完成后,我们就可以直接开始使用了。在需要使用值的浪仔麻将源码地方,直接注入 MessageSource 实例即可。在接口调用时,通过请求头的 Accept-Language 来配置当前的环境,我这里通过 POSTMAN 来进行测试,结果如下:默认情况下,系统是根据请求头的 Accept-Language 字段来判断当前的语言环境的。通过 POSTMAN 来设置 Accept-Language 的值,然后调用接口,就可以看到返回的语言文字。
2.2 自定义切换
有的小伙伴觉得切换参数放在请求头里边好像不太方便,那么也可以自定义解析方式。例如参数可以当成普通参数放在地址栏上,通过如下配置可以实现我们的需求。在配置中,我们首先提供了一个 SessionLocaleResolver 实例,这个实例会替换掉默认的jetty 8 源码分析 AcceptHeaderLocaleResolver,不同于 AcceptHeaderLocaleResolver 通过请求头来判断当前的环境信息,SessionLocaleResolver 将客户端的 Locale 保存到 HttpSession 对象中,并且可以进行修改(这意味着当前环境信息,前端给浏览器发送一次即可记住,只要 session 有效,浏览器就不必再次告诉服务端当前的环境信息)。
好了,配置完成后,启动项目,访问方式如下:通过在请求中添加 lang 来指定当前环境信息。这个指定只需要一次即可,也就是说,在 session 不变的情况下,下次请求可以不必带上 lang 参数,服务端已经知道当前的环境信息了。
2.3 其他自定义
默认情况下,我们的配置文件放在 resources 目录下,如果大家想自定义,也是可以的,例如定义在 resources/in 目录下。但是这种定义方式系统就不知道去哪里加载配置文件了,此时还需要 application.properties 中进行额外配置(注意这是一个相对路径)。另外还有一些编码格式的配置等,内容如下:spring.messages.cache-duration 表示 messages 文件的缓存失效时间,如果不配置则缓存一直有效。spring.messages.fallback-to-system-locale 属性则略显神奇,网上竟然看不到一个明确的答案,后来翻了一会源码才看出端倪。这个属性的作用在org.springframework.context.support.AbstractResourceBasedMessageSource#getDefaultLocale 方法中生效:从这段代码可以看出,在找不到当前系统对应的资源文件时,如果该属性为 true,则会默认查找当前系统对应的资源文件,否则就返回 null,返回 null 之后,最终又会调用到系统默认的 messages.properties 文件。
3. LocaleResolver
国际化这块主要涉及到的组件是 LocaleResolver,这是一个开放的接口,官方默认提供了四个实现。当前该使用什么环境,主要是通过 LocaleResolver 来进行解析的。
我们来看看 LocaleResolver 的继承关系:虽然中间有几个抽象类,不过最终负责实现的其实就四个:AcceptHeaderLocaleResolver、SessionLocaleResolver、FixedLocaleResolver、CookieLocaleResolver。接下来我们就对这几个类逐一进行分析。
3.1 AcceptHeaderLocaleResolver
AcceptHeaderLocaleResolver 直接实现了 LocaleResolver 接口,我们来看它的 resolveLocale 方法:再来看看它的 setLocale 方法,直接抛出异常,意味着通过请求头处理 Locale 是不允许修改的。
3.2 SessionLocaleResolver
SessionLocaleResolver 的实现多了一个抽象类 AbstractLocaleContextResolver,AbstractLocaleContextResolver 中增加了对 TimeZone 的支持。我们先来看下 AbstractLocaleContextResolver:可以看到,多了一个 TimeZone 属性。从请求中解析出 Locale 还是调用了 resolveLocaleContext 方法,该方法在子类中被实现,另外调用 setLocaleContext 方法设置 Locale,该方法的实现也在子类中。
我们来看下它的子类 SessionLocaleResolver:直接从 Session 中获取 Locale,默认的属性名是SessionLocaleResolver.class.getName() + ".LOCALE",如果 session 中不存在 Locale 信息,则调用 determineDefaultLocale 方法去加载 Locale,该方法会首先找到 defaultLocale,如果 defaultLocale 不为 null 就直接返回,否则就从 request 中获取 Locale 返回。再来看 setLocaleContext 方法,就是将解析出来的 Locale 保存起来,保存到 Session 中即可。
3.3 FixedLocaleResolver
FixedLocaleResolver 有三个构造方法,无论调用哪一个,都会配置默认的 Locale:要么自己传 Locale 进来,要么调用 Locale.getDefault() 方法获取默认的 Locale。再来看 resolveLocale 方法:这个应该就不用解释了吧。需要注意的是它的 setLocaleContext 方法,直接抛异常出来,也就意味着 Locale 在后期不能被修改。
3.4 CookieLocaleResolver
CookieLocaleResolver 和 SessionLocaleResolver 比较类似,只不过存储介质变成了 Cookie,其他都差不多,松哥就不再重复介绍了。
4.附录
搜刮了一个语言简称表,分享给各位小伙伴:
5.小结
好啦,今天主要和小伙伴们聊了下 SpringMVC 中的国际化问题,以及 LocaleResolver 相关的源码,相信大家对 SpringMVC 的理解应该又更近一步了吧。