Springboot国际化信息(i18n)解析

国际化信息理解

国际化信息也称为本地化信息 。 Java 通过 java.util.Locale 类来表示本地化对象,它通过 “语言类型” 和 “国家/地区” 来创建一个确定的本地化对象 。举个例子吧,比如在发送一个具体的请求的时候,在header中设置一个键值对:"Accept-Language":"zh",通过Accept-Language对应值,服务器就可以决定使用哪一个区域的语言,找到相应的资源文件,格式化处理,然后返回给客户端。

MessageSource

Spring 定义了 MessageSource 接口,用于访问国际化信息。

  • getMessage(String code, Object[] args, String defaultMessage, Locale locale)
  • getMessage(String code, Object[] args, Locale locale)
  • getMessage(MessageSourceResolvable resolvable, Locale locale)

 

 MessageSourceAutoConfiguration

 springboot提供了国际化信息自动配置类,配置类中注册了ResourceBundleMessageSource实现类。

 1 @Configuration
 2 @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
 3 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 4 @Conditional(ResourceBundleCondition.class)
 5 @EnableConfigurationProperties
 6 public class MessageSourceAutoConfiguration {
 7 
 8     private static final Resource[] NO_RESOURCES = {};
 9 
10     @Bean
11     @ConfigurationProperties(prefix = "spring.messages")
12     public MessageSourceProperties messageSourceProperties() {
13         return new MessageSourceProperties();
14     }
15 
16     @Bean
17     public MessageSource messageSource(MessageSourceProperties properties) {
18         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
19         if (StringUtils.hasText(properties.getBasename())) {
20             messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
21                     StringUtils.trimAllWhitespace(properties.getBasename())));
22         }
23         if (properties.getEncoding() != null) {
24             messageSource.setDefaultEncoding(properties.getEncoding().name());
25         }
26         messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
27         Duration cacheDuration = properties.getCacheDuration();
28         if (cacheDuration != null) {
29             messageSource.setCacheMillis(cacheDuration.toMillis());
30         }
31         messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
32         messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
33         return messageSource;
34     }
35 
36     protected static class ResourceBundleCondition extends SpringBootCondition {
37 
38         private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
39 
40         @Override
41         public ConditionOutcome getMatchOutcome(ConditionContext context,
42                 AnnotatedTypeMetadata metadata) {
43             String basename = context.getEnvironment()
44                     .getProperty("spring.messages.basename", "messages");
45             ConditionOutcome outcome = cache.get(basename);
46             if (outcome == null) {
47                 outcome = getMatchOutcomeForBasename(context, basename);
48                 cache.put(basename, outcome);
49             }
50             return outcome;
51         }
52 
53         private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
54                 String basename) {
55             ConditionMessage.Builder message = ConditionMessage
56                     .forCondition("ResourceBundle");
57             for (String name : StringUtils.commaDelimitedListToStringArray(
58                     StringUtils.trimAllWhitespace(basename))) {
59                 for (Resource resource : getResources(context.getClassLoader(), name)) {
60                     if (resource.exists()) {
61                         return ConditionOutcome
62                                 .match(message.found("bundle").items(resource));
63                     }
64                 }
65             }
66             return ConditionOutcome.noMatch(
67                     message.didNotFind("bundle with basename " + basename).atAll());
68         }
69 
70         private Resource[] getResources(ClassLoader classLoader, String name) {
71             String target = name.replace('.', '/');
72             try {
73                 return new PathMatchingResourcePatternResolver(classLoader)
74                         .getResources("classpath*:" + target + ".properties");
75             }
76             catch (Exception ex) {
77                 return NO_RESOURCES;
78             }
79         }
80 
81     }
82 
83 }
View Code

首先MessageSource配置生效依靠一个ResourceBundleCondition条件,从环境变量中读取spring.messages.basename对应的值,默认值是messages,这个值就是MessageSource对应的资源文件名称,资源文件扩展名是.properties,然后通过PathMatchingResourcePatternResolver从“classpath*:”目录下读取对应的资源文件,如果能正常读取到资源文件,则加载配置类。

 

 springmvc自动装配配置类,注册了一个RequestContextFilter过滤器。

 每一次请求,LocaleContextHolder都会保存当前请求的本地化信息。

 通过MessageSourceAccessor根据code获取具体信息时,如果默认配置的本地化对象为空,则通过LocaleContextHolder获取。

 上图的messageSource是应用程序上下文对象(本文创建的是GenericWebApplicationContext实例),该messageSource对象会调用ResourceBundleMessageSource实例获取具体信息。

ValidationAutoConfiguration

参数校验hibernate-validator是通过这个自动装配加载进来的。

 1 @Configuration
 2 @ConditionalOnClass(ExecutableValidator.class)
 3 @ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
 4 @Import(PrimaryDefaultValidatorPostProcessor.class)
 5 public class ValidationAutoConfiguration {
 6 
 7     @Bean
 8     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 9     @ConditionalOnMissingBean(Validator.class)
10     public static LocalValidatorFactoryBean defaultValidator() {
11         LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
12         MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
13         factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
14         return factoryBean;
15     }
16 
17     @Bean
18     @ConditionalOnMissingBean
19     public static MethodValidationPostProcessor methodValidationPostProcessor(
20             Environment environment, @Lazy Validator validator) {
21         MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
22         boolean proxyTargetClass = environment
23                 .getProperty("spring.aop.proxy-target-class", Boolean.class, true);
24         processor.setProxyTargetClass(proxyTargetClass);
25         processor.setValidator(validator);
26         return processor;
27     }
28 
29 }
View Code

MethodValidationPostProcessor这个后置处理处理方法里单个参数校验的注解(JSR和Hibernate validator的校验只能对Object的属性(也就是Bean的域)进行校验,不能对单个的参数进行校验。)。

LocalValidatorFactoryBean实现了javax.validation.ValidatorFactory和javax.validation.Validator这两个接口,以及Spring的org.springframework.validation.Validator接口,你可以将这些接口当中的任意一个注入到需要调用验证逻辑的Bean里。

 默认情况下,LocalValidatorFactoryBean创建的validator使用PlatformResourceBundleLocator获取资源的绑定关系,获取的资源名称是:ValidationMessages

 用户自定义的校验信息放在项目classpath目录下。

另外hibernate-validator还会加载默认的校验资源文件,名称是:org.hibernate.validator.ValidationMessages。可以看到,默认的校验资源捆绑文件包含了不同区域的信息的配置。

通过LocalValidatorFactoryBean获取的validator是如何根据不同的地区加载不同校验资源文件呢?hibernate-validator暴露了一个消息插补器(MessageInterpolator),spring正是重新代理这个消息插补器。

 通过LocaleContextMessageInterpolator源码,可以看到最终还是通过LocaleContextHolder获取当前时区信息。

 

是否可以自定义国际化校验的资源信息呢?当然是肯定的,我们只需要重写LocalValidatorFactoryBean类型bean的创建过程,通过setValidationMessageSource方法指定自定义的资源信息。

MessageSource测试

基础测试

建立Resouce bundle messages

编写message source测试方法,从request中获取当前Locale值

 编写测试类,指定当前请求的Locale值或者设置请求头的header值:Accept-Language:zh

根据测试类中请求的Locale值不同,获取到的文本也不同。

格式化测试

建立Resouce bundle messages

编写message source测试方法,从request中获取当前Locale值

 编写测试类,指定当前请求的Locale值或者设置请求头的header值:Accept-Language:zh

 

根据测试类中请求的Locale值不同,获取到的格式化的文本也不同。

静态message source测试

动态注册message(可区分Locale),可用于自定义message source。

编写测试的方法,通过MessageSourceAccessor访问。

 编写测试类,获取自定义message source中的信息。

根据测试类中请求的Locale值不同,获取到的文本也不同。

 

转载于:https://www.cnblogs.com/hujunzheng/p/11037577.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言一看就能上手的干货!你确定你不来看吗?

本地环境设置 如果您想要设置 C 语言环境&#xff0c;您需要确保电脑上有以下两款可用的软件&#xff0c;文本编辑器和 C 编译器。 文本编辑器 这将用于输入您的程序。文本编辑器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。文本编辑器的名称…

10万码农五年的C语言笔记!你现在知道别人为什么这么优秀了吗?

c语言对许多同学来说确实是一门比较难学的课程&#xff0c;不仅抽象&#xff0c;而且繁琐&#xff0c;但这又是一门不得不学的课程。前两节可能还有兴致听一听&#xff0c;然而&#xff0c;再过几节课就是一脸蒙比。凭空要想出一道题的算法和程序&#xff0c;根本无从下手。 所…

C语言/C++编程学习:C语言环境设置!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言指针原来也可以这么的通俗易懂!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言过时了?你在做梦?

为什么要使用C语言&#xff1f; 在过去的四十年里&#xff0c;C语言已经成为世界上最流行、最重要的一种编程语言。 C是一种融合了控制特性的现代语言&#xff0c;而我们已发现在计算机科学的理论和实践中&#xff0c;控制特性是很重要的。其设计使得用户可以自然地采用自顶向…

C语言深入理解!助你向大佬迈进!

Dennis Ritchie 过世了&#xff0c;他发明了C语言&#xff0c;一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言&#xff0c;今天很多语言都受到C的影响&#xff0c;C&#xff0c;Java&#xff0c;C#&#xff0c;Perl&#xff0c; PHP&#xf…

【初涉C语言】程序员欢迎来到C语言的世界!

计算机发展史 机器语言所有的代码里面只有0和1优点&#xff1a;直接对硬件产生作用&#xff0c;程序的执行效率非常高缺点&#xff1a;指令又多又难记、可读性差、无可移植性汇编语言符号化的机器语言&#xff0c;用一个符号&#xff08;英文单词、数字&#xff09;来代表一条…

C语言和C++的区别整理详解!

c和c主要区别 根据书中的描述&#xff0c;进行了整理 推荐一个我自己的C/C交流裙815393895 1、 源代码文件的扩展名 摘自1.4.1 C实现源代码文件的扩展名UNIXC、cc、cxx、cGNU CC、cc、cxx、cpp、cDigital Marscpp、cxxBorland CcppWatcomcppMicrosoft Visual Ccpp、cxx、cc…

揭示C语言函数调用的本质解析

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得到…

C语言/C++编程学习:不找C/C++的工作也要学C/C++的原因

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

【网络攻防】精通C语言的黑客才是真正的黑客!

精通C语言的黑客才是真正的黑客 黑客界&#xff0c;有两样重要的课程&#xff0c;一是计算机的本质&#xff0c;二是编译原理。相对于汇编等底层语言&#xff0c;它简单&#xff1b;相对于其它高级语言&#xff0c;它更为接近计算机&#xff1b;同样它对黑客的两大课程很有帮助…

我两小时学完指针,你学会数组/指针与函数需要多久?

数组与函数&#xff1a; 这段函数中 函数的参数是数组&#xff0c;注意数组作为函数参数时&#xff0c;数组名和数组元素个数时分别传递的。 指针与函数&#xff1a; 这段函数中的参数是指针变量&#xff0c;传入的是数组的数组名或者首元素的地址&#xff0c;然后用引领操作…

C语言发展历史,C语言特点,C语言利于弊,入门须知三招

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 这些是C/C能做的 服务器开发工程师、人工智能、云计算工程师、信息安全&#xff08;黑客反黑客&#xff09;、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程…

程序员怎么看待C语言?最伟大?最落后?

一&#xff0c;前言 对我来说&#xff0c;C语言应该可以算得上是世界上最伟大的编程语言。全中国口气最大的程序员&#xff0c;业界称之为“垠神”&#xff0c;曾经发过文章吐槽过业界各种主流的编程语言&#xff08;对Java&#xff0c;的Python稍微宽容一些&#xff09;&…

如何学习C语言?就是这么简单粗暴!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的。 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理…

C/C++对编程的重要性!其他编程语言都是弟弟!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言其实不难,只是你没有找对方法!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

想学好C语言?先把基础打好再说吧!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言新手最常见的问题!你在这里跌倒过吗?

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

用C语言做出你自己的七夕表白程序!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…