spring-boot-2.0.3源码篇 - 国际化
前言
针对spring boot,网上已有很多优质的系列教程,我就不再班门弄斧了(实际上是担心没别人写的好,哈哈哈!)。但是还是想蹭蹭spring boot的热度,即使不考虑微服务,spring boot还是有很多可取优点的,比如自动化配置、系列Starters简化maven的依赖管理等。本系列主要是将工作中涉及到的一些功能利用spring boot整合到一起(工作中还没用到spring-boot)。
maven-ssm-web中的内容会陆续集成进来,最近几篇博客会先介绍一些maven-ssm-web中没有的新内容(因为比较熟嘛!);maven-ssm-web最近会停更,如果有朋友仍需要,还是会继续更新的;spring boot的集成工程是:spring-boot-integrate,系列工程则是: spring-boot-2.0.3。
该系列工程都是基于spring-boot-2.0.3;本文是第一篇,先来点简单的,讲讲spring boot中的国际化,工程地址:spring-boot-i18n
基本版
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com .lee</groupId>
<artifactId>spring-boot-i18n</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<java.version>1.8</java.version>
</properties> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8880
spring:
#国际化配置
messages:
encoding: utf-8
basename: i18n/messages
#thymeleaf配置
thymeleaf:
cache: false
I18nConfig.java
package com.lee.i18n.config; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; @Configuration
public class I18nConfig { // 配置cookie语言解析器
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setCookieMaxAge(3600); // cookie有效时长,单位秒
resolver.setCookieName("Language"); //设置存储的Cookie的name为Language
return resolver;
} // 配置一个拦截器,拦截国际化语言的变化
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor()).addPathPatterns("/**");
}
};
}
}
LoginController.java
package com.lee.i18n.controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
public class LoginController { @RequestMapping("/login")
public String login() {
return "login";
} @RequestMapping("/index")
public String index() {
return "index";
}
}
messages.properties
index.welcome=欢迎 login.username=登录名
login.password=密码
login.login=登录
messages_en_US.properties
index.welcome=welcome login.username=username
login.password=password
login.login=login
messages_zh_CN.properties
index.welcome=欢迎 login.username=登录名
login.password=密码
login.login=登录
当如上文件配置好之后(其他的可以去spring-boot-i18n拉取),都配置好后,工程跑起来,我们来看看结果,是否达到国际化效果呢? 答案是肯定的嘛!
高级版
基本版有一个缺点,就是国际化资源都写在了一个文件中:messages*.properties,内容都写在一个文件中有一个致命的缺点:文件越大,越难以维护;
那么高级版高级在哪了? 你想的没错,就是将资源按某种性质或者功能划分成资源文件夹,再在资源文件夹下放具体的资源文件,如下图
改动的内容已标明,具体改动的内容可以去spring-boot-i18n拉取;工程跑起来,我们看看结果
源码探究
从两个容器的初始化来看整个过程,是哪两个容器了,一个是spring根容器、一个是spring mvc容器,spring根容器也就是根上下文:WebApplicationContext,spring mvc容器即是:DispatcherServlet;
spring根容器初始化
我们从main函数入手,如下图
initMessageSource():初始化国际化资源,finishBeanFactoryInitialization(beanFactory) 实例化非延迟初始化的bean;spring容器初始化的内容还是非常多的,有兴趣的朋友可以跟着断点调试详细看看初始化话过程; 最终全部bean定义都放在了DefaultListableBeanFactory的beanDefinitionMap中了,后续则从beanDefinitionMap中获取bean定义进行实例化。
spring mvc容器初始化
我们都知道spring mvc的核心类就是DispatcherServlet,我们就从他入手,如下图:
从DispatcherServlet继承关系可知,HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法,我们可以以此为入口来追踪DispatcherServlet的初始化过程;DispatcherServlet中的initStrategies方法比较重要,而其中initLocaleResolver(context)和initHandlerMappings(context)和本文的国际化有直接关系,initLocaleResolver(context)将我们自己定义的localeResolver绑到了DispatcherServlet的属性localeResolver中;而initHandlerMappings(context)又将我们自己新增的拦截器LocaleChangeInterceptor添加到了DispatcherServlet的handlerMappings中;
是不是有种很美妙的预感,我们自定义的一些bean都关联到了DispatcherServlet中,而我们的请求url又必须经过DispatcherServlet,这是不是巧合? 很显然这不是!如果你还是一头雾水,对不起! 我们接着往下看......
请求过程
从DispatcherServlet的继承关系可知,请求会经过DispatcherServlet的doService方法,doService会将DispatcherServlet中的localeResolver(也就是我们定义的CookieLocaleResolver对象)绑定到当前request对象中,然后再调用doDispatch进行请求的转发;
LocaleChangeInterceptor的类继承图
可知它继承了HandlerInterceptor,并重写了preHandle,我们就从LocaleChangeInterceptor的preHandle方法开始(请求肯定会经过此方法)阅读源码,打断点追踪,如下如
既然能通过locale参数感知语言的变化,那么肯定也能根据语言加载对应的资源,从而实现国际化(具体如何加载的需要大家自己去阅读源码了!)
源码阅读就此告一段落,不是特别细,只是提供了一个主体流程;强烈建议大家阅读源码的时候,进行断点调试跟踪,不容易跟丢!
参考
第三章 DispatcherServlet详解 ——跟开涛学SpringMVC
spring-boot-2.0.3源码篇 - 国际化的更多相关文章
- Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览
关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...
- springboot2.0.3源码篇 - 自动配置的实现,发现也不是那么复杂
前言 开心一刻 女儿: “妈妈,你这么漂亮,当年怎么嫁给了爸爸呢?” 妈妈: “当年你爸不是穷嘛!‘ 女儿: “穷你还嫁给他!” 妈妈: “那时候刚刚毕业参加工作,领导对我说,他是我的扶贫对象,我年轻 ...
- Spring Boot 揭秘与实战 源码分析 - 工作原理剖析
文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...
- Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机
文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...
- Spring Boot 注解之ObjectProvider源码追踪
最近依旧在学习阅读Spring Boot的源代码,在此过程中涉及到很多在日常项目中比较少见的功能特性,对此深入研究一下,也挺有意思,这也是阅读源码的魅力之一.这里写成文章,分享给大家. 自动配置中的O ...
- spring-boot-2.0.3源码篇 - pageHelper分页,绝对有值得你看的地方
前言 开心一刻 说实话,作为一个宅男,每次被淘宝上的雄性店主追着喊亲,亲,亲,这感觉真是恶心透顶,好像被强吻一样.........更烦的是我每次为了省钱,还得用个女号,跟那些店主说:“哥哥包邮嘛么叽. ...
- spring-boot-2.0.3源码篇 - filter的注册,值得一看
前言 开心一刻 过年女婿来岳父家走亲戚,当时小舅子主就问:姐夫,你什么时候能给我姐幸福,让我姐好好享受生活的美好.你们这辈子不准备买一套大点的房子吗?姐夫说:现在没钱啊!不过我有一个美丽可爱的女儿,等 ...
- SpringBoot 源码解析 (八)----- Spring Boot 精髓:事务源码解析
本篇来讲一下SpringBoot是怎么自动开启事务的,我们先来回顾一下以前SSM中是如何使用事务的 SSM使用事务 导入JDBC依赖包 众所周知,凡是需要跟数据库打交道的,基本上都要添加jdbc的依赖 ...
- spring-boot-2.0.3源码篇 - @Configuration、Condition与@Conditional
前言 开心一刻 一名劫匪慌忙中窜上了一辆车的后座,上车后发现主驾和副驾的一男一女疑惑地回头看着他,他立即拔出枪威胁到:“赶快开车,甩掉后面的警车,否则老子一枪崩了你!”,于是副驾上的男人转过脸对那女的 ...
随机推荐
- python 06函数
python中函数定义方法: def test(x): "The function definitions" x+=1 return x def:定义函数的关键字test:函数名( ...
- C#实现局部峰值查找,功能对应Matlab中的findpeaks.m(转)
相关算法的原理参考Ronny,地址:图像分析:投影曲线的波峰查找,这里感谢下原作者. 参照C++的代码实现,我用C#翻译了下,其实原理也很简单的,下面放相关实现代码: private double[] ...
- C#sealed和final
sealed用于类时,该类被称为密封类,密封类不能被继承: C#提出了密封方法(sealed method) 的概念,以防止在方法所在类的派生类中对该方法的重载.对方法可以使用sealed 修饰符,这 ...
- centos7 编译安装nginx+tcp转发
一.依赖 1. gcc 安装安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装: yum install gcc-c++ 2. PCRE pc ...
- Spring通过在META-INF/spring.handlers中的属性进行配置文件解析
在Spring的入口函数refresh()之中进行的. AbstractApplicationContext ConfigurableListableBeanFactory beanFactory = ...
- Zip包解压工具类
最近在做项目的自动检测离线升级,使用到了解压zip包的操作,本着拿来主义精神,搞了个工具类(同事那边拿的),用着还不错. package com.winning.polaris.admin.utils ...
- elasticsearch 安装,以及遇到的问题总结
系统.软件环境: Centos 6.5 elasticsearch 6.1.1 elasticsearch 安装的话是很简单的,但是安装完成启动的时候报错,下面我就一一的来描述错误,并提供相应的解决方 ...
- eclipse经常出现——未响应!!!
现象:启动eclipse缓慢,启动完成经常出现未响应情况.偶然在打开一个项目时候,也出现未响应. 原因:虚拟内存不足,或者电脑本身内存不足.但是目前绝大多数PC而言,内存应该是充足的,因此可以修改虚拟 ...
- 记录使用 Cake 进行构建并制作 nuget 包
书接上一回(https://www.cnblogs.com/h82258652/p/4898983.html)?[手动狗头] 前段时间折腾了一下,总算是把我自己的图片缓存控件(https://gith ...
- iostat 命令详解
前言 话说搞运维的人没有两把"刷子",都不好意思上服务器操作.还好,我还不是搞运维的,我一直都自诩是开发人员,奈何现在的东家运维人员"水"的一比,还要我这个自诩 ...