前言

针对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参数感知语言的变化,那么肯定也能根据语言加载对应的资源,从而实现国际化(具体如何加载的需要大家自己去阅读源码了!)

  源码阅读就此告一段落,不是特别细,只是提供了一个主体流程;强烈建议大家阅读源码的时候,进行断点调试跟踪,不容易跟丢!

参考

  自己动手在Spring-Boot上加强国际化功能

  第三章 DispatcherServlet详解 ——跟开涛学SpringMVC

spring-boot-2.0.3源码篇 - 国际化的更多相关文章

  1. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

  2. springboot2.0.3源码篇 - 自动配置的实现,发现也不是那么复杂

    前言 开心一刻 女儿: “妈妈,你这么漂亮,当年怎么嫁给了爸爸呢?” 妈妈: “当年你爸不是穷嘛!‘ 女儿: “穷你还嫁给他!” 妈妈: “那时候刚刚毕业参加工作,领导对我说,他是我的扶贫对象,我年轻 ...

  3. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  4. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  5. Spring Boot 注解之ObjectProvider源码追踪

    最近依旧在学习阅读Spring Boot的源代码,在此过程中涉及到很多在日常项目中比较少见的功能特性,对此深入研究一下,也挺有意思,这也是阅读源码的魅力之一.这里写成文章,分享给大家. 自动配置中的O ...

  6. spring-boot-2.0.3源码篇 - pageHelper分页,绝对有值得你看的地方

    前言 开心一刻 说实话,作为一个宅男,每次被淘宝上的雄性店主追着喊亲,亲,亲,这感觉真是恶心透顶,好像被强吻一样.........更烦的是我每次为了省钱,还得用个女号,跟那些店主说:“哥哥包邮嘛么叽. ...

  7. spring-boot-2.0.3源码篇 - filter的注册,值得一看

    前言 开心一刻 过年女婿来岳父家走亲戚,当时小舅子主就问:姐夫,你什么时候能给我姐幸福,让我姐好好享受生活的美好.你们这辈子不准备买一套大点的房子吗?姐夫说:现在没钱啊!不过我有一个美丽可爱的女儿,等 ...

  8. SpringBoot 源码解析 (八)----- Spring Boot 精髓:事务源码解析

    本篇来讲一下SpringBoot是怎么自动开启事务的,我们先来回顾一下以前SSM中是如何使用事务的 SSM使用事务 导入JDBC依赖包 众所周知,凡是需要跟数据库打交道的,基本上都要添加jdbc的依赖 ...

  9. spring-boot-2.0.3源码篇 - @Configuration、Condition与@Conditional

    前言 开心一刻 一名劫匪慌忙中窜上了一辆车的后座,上车后发现主驾和副驾的一男一女疑惑地回头看着他,他立即拔出枪威胁到:“赶快开车,甩掉后面的警车,否则老子一枪崩了你!”,于是副驾上的男人转过脸对那女的 ...

随机推荐

  1. BZOJ4386[POI2015]Wycieczki / Luogu3597[POI2015]WYC - 矩乘

    Solution 想到边权为$1$的情况直接矩乘就可以得出长度$<=t$ 的路径条数, 然后二分check一下即可 但是拓展到边权为$2$,$3$ 时, 需要新建节点 $i+n$ 和 $i+2n ...

  2. php正则提取html图片(img)src地址与任意属性的方法

    <?php /*PHP正则提取图片img标记中的任意属性*/ $str = '<center><img src="/uploads/images/2017020716 ...

  3. 深入理解JVM(二)Java内存区域

    2.1 C.C++内存管理是由开发人员管理,而Java则交给了JVM进行自动管理 2.2 JVM运行时数据区:方法区.堆(运行时线程共享),虚拟机栈.本地方法栈.程序计数器(运行时线程隔离,私有) 1 ...

  4. 2019.03.01 bzoj2555: SubString(sam+lct)

    传送门 题意简述: 要求在线支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 思路: 考虑用lctlctlct来动态维护samsa ...

  5. ubuntu 配置jdk报错解决办法

    vi /etc/profile ,添加如下代码 export JAVA_HOME=/home/mark/android/jdk1.8 export JRE_HOME=/home/mark/androi ...

  6. _ZNote_Qt_QtCreator_Tips_粘贴_历史剪切板

    发现 快捷键 Shift+Command + V 能够出现历史剪切板. 厉害了我的歌

  7. Beta冲刺 (3/7)

    Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 熟悉并编写小程序的自定义控件 编辑文 ...

  8. Django Model 基础

    程序涉及到数据库相关操作时,一般都会这样: 创建数据库,设计表结构和字段 使用 pymysql 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作 import pymysq ...

  9. 深度学习框架caffe/CNTK/Tensorflow/Theano/Torch的对比

    在单GPU下,所有这些工具集都调用cuDNN,因此只要外层的计算或者内存分配差异不大其性能表现都差不多. Caffe: 1)主流工业级深度学习工具,具有出色的卷积神经网络实现.在计算机视觉领域Caff ...

  10. Spring AOP的实现及源码解析

    在介绍AOP之前,想必很多人都听说AOP是基于动态代理和反射来实现的,那么在看AOP之前,你需要弄懂什么是动态代理和反射及它们又是如何实现的. 想了解JDK的动态代理及反射的实现和源码分析,请参见下面 ...