1.SpringBoot 中 localeResolver 的实现原理
WebMvcAutoConfiguration 类中
1、WebMvcAutoConfiguration 是SpringBoot中的 Web方面的自动配置类。
2、当用户没有创建自己的 localeResolver
时,这个配置方法就会生效,从而产生一个localeResolver。(配置自己的localeResolver时,Bean名必须为localeResolver)。
3、spring.web.locale-resolver、spring.mvc.locale-resolver
的配置属性都有两个值可供选择。(fixed、accept_header)。
4、选择属性值 fixed,表示 Locale 区域对象是 固定的。
5、当属性值为fixed,应该搭配spring.web.locale、spring.mvc.locale 这两个配置属性一起使用,给出固定的Locale
区域对象。
6、假如属性值为fixed,又没有搭配上面两个属性之一,则因为上面两个属性没有默认值,则Locale
区域对象将会使用运行主机的默认语言环境生成一个Locale 区域对象。
6、从以下的方法的执行流程可以看出,spring.web.locale-resolver 优先级比 spring.mvc.locale-resolver
高一些。
7、因为spring.web.locale-resolver、spring.mvc.locale-resolver 它们的 默认值 都为
accept_header ,所以,只要不更改配置,默认就不是固定的Locale 区域对象。就会继续执行最下面的部分。
8、此时spring.web.locale、spring.mvc.locale
这两个配置属性,假如存在,就会成为AcceptHeaderLocaleResolver 的默认的Locale 区域对象。
并在请求响应的请求头中没有Accept-Language这个属性时,成为AcceptHeaderLocaleResolver返回的Locale 区域对象。
@Bean
@ConditionalOnMissingBean(
name = {"localeResolver"} //假如SpringBoot中没有这个 localeResolver Bean,就加载这个自动配置Bean
)
public LocaleResolver localeResolver() {
//假如配置文件中有 spring.web.locale-resolver=fixed,就表示是固定的 Locale 配置
//(这个属性默认值为 ACCEPT_HEADER,WebProperties 类中)
if (this.webProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.WebProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.webProperties.getLocale());
//假如配置文件中有 spring.mvc.locale-resolver=fixed,就表示是固定的 Locale 配置
//(这个属性默认值为 ACCEPT_HEADER,WebMvcProperties 类中)
} else if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
} else {
//使用请求对象的请求头中的Accept-Language属性来生成Locale
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
Locale locale = this.webProperties.getLocale() != null ? this.webProperties.getLocale() : this.mvcProperties.getLocale();
localeResolver.setDefaultLocale(locale);
return localeResolver;
}
}
FixedLocaleResolver
类中的resolveLocale
方法
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = this.getDefaultLocale(); //获取默认的配置的 Locale
if (locale == null) {
locale = Locale.getDefault(); //根据主机的语言环境生成一个 Locale
}
return locale;
}
AcceptHeaderLocaleResolver
类中的resolveLocale
方法
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = this.getDefaultLocale(); //获取默认的配置的 Locale
if (defaultLocale != null && request.getHeader("Accept-Language") == null) { //默认配置的Locale 不为空,且请求头中没有Accept-Language 属性
return defaultLocale;
} else {
Locale requestLocale = request.getLocale(); //根据 Accept-Language 标头,返回客户端将接受内容的首选。如果客户端请求未提供 Accept-Language 标头,则此方法返回服务器的默认语言环境
List<Locale> supportedLocales = this.getSupportedLocales(); //获取支持的Locale 队列
if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) { //Locale 不为空,且不包含请求对象中获取的Locale
Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
} else {
return defaultLocale != null ? defaultLocale : requestLocale;
}
} else {
return requestLocale;
}
}
}
2.两种国际化方式
1.什么都不用配置,使用默认的 LocaleResolver 的实现逻辑
从上面的AcceptHeaderLocaleResolver 方法,返回的LocaleResolver
的逻辑可知,我们最终会得到AcceptHeaderLocaleResolver,这样根据请求对象中的请求头中的Accept-
Language属性,来返回Locale 区域对象的 LocaleResolver 。
2.使用自己创建的 LocaleResolver 来创建自己的逻辑,去实现国际化
实现逻辑
逻辑为:我们将根据请求中的 lang
参数,进而形成不同区域的Locale
对象,来判断页面中的数据,究竟是显示(zh_CN)中文还是(en_US)英文。
3.具体实现步骤
新建一个名叫“i18n”的包,我们用来存放国际化配置,然后在这个包下,我们再创建几个properties的配置文件(文件名语言区域.properties),用来配置语言:
点击login_en_US.properties 的配置文件,然后点击下边如图所示的Resource Bundle按钮
注:新版IDEA可能需要安装Resource Bundle Editor插件才可以使用Resource Bundle按钮
接下来填写相应的配置,添加login.tip 并将个文件中对应的tip内容填写完整
使用ResourceBundleMessageSource管理国际化资源文件
在 application.properties (也可以是application.yaml)中添加如下代码
spring:
messages:
basename: i18n.login
thymeleaf中获取property的内容语法:
Message Expressions: #{...}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Login</title>
</head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<h1 th:text="#{login.tip}" class="h3 mb-3 font-weight-normal">Please sign in</h1>
<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<input type="checkbox" value="remember-me"> [[#{login.remeber}]]
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn} ]]</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
<a class="btn btn-sm" th:href="@{/index(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index(l='en_US')}">English</a>
</form>
</body>
</html>
注意 :input输入框不能使用th:text取值,text是标签里面的内容,input输入框是字节数,没有标签体
可使用thymleaf行内表达式,双括号添加表达式
<p>Hello, [[${session.user.name}]]!</p>
可能会出现乱码问题,需要在settings->Editor->File Encodings->将properties的编码格式改成utf-8
实现自定义LocaleResolver
/**
* @Description: 可以在链接上面携带区域信息
* @Author: zhangxy
*
*/
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String l = httpServletRequest.getParameter("l");
// 获取系统默认的
Locale locale = Locale.getDefault();
// 如果参数带了区域信息,则使用参数的
if (!StringUtils.isEmpty(l)) {
String[] s = l.split("_");
locale = new Locale(s[0], s[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Locale locale) {
}
}
然后向mvc配置类中注入LocaleResolver
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//这个方法是用来实现页面跳转控制的 就比如你@RequestMapping("/") 然后方法返回的就是setviewname的值
//这里配置后就不用再去@Controller实现跳转了
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/index").setViewName("index");
}
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
展示: