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.pswp.cn/news/531189.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531189.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531189.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

看了就知道為什么別人C語言學習效率那么高了

談及C語言&#xff0c;我想C語言功能強大都應該知道、應用廣泛&#xff0c;一旦掌握了后&#xff0c;你就可以理直氣壯地對他人說“我是電腦高手&#xff01;”&#xff0c;而且以后若是再自學其他語言就顯得輕而易舉了。憂慮的是&#xff0c;C語言般博大精深&#xff0c;太難學…

C語言一看就能上手的干貨!你確定你不來看嗎?

本地環境設置 如果您想要設置 C 語言環境&#xff0c;您需要確保電腦上有以下兩款可用的軟件&#xff0c;文本編輯器和 C 編譯器。 文本編輯器 這將用于輸入您的程序。文本編輯器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。文本編輯器的名稱…

C語言爆炸干貨,小白你還不來看看嘛!

①&#xff1a;數據類型 int(整型)&#xff0c;short int(短整型)&#xff0c;long int(長整型)&#xff0c; char(字符型)&#xff0c;float&#xff08;單精度浮點型&#xff09; double&#xff08;雙精度浮點型&#xff09; C語言編程交流群815393895 ②&#xff1a;邏…

10萬碼農五年的C語言筆記!你現在知道別人為什么這么優秀了嗎?

c語言對許多同學來說確實是一門比較難學的課程&#xff0c;不僅抽象&#xff0c;而且繁瑣&#xff0c;但這又是一門不得不學的課程。前兩節可能還有興致聽一聽&#xff0c;然而&#xff0c;再過幾節課就是一臉蒙比。憑空要想出一道題的算法和程序&#xff0c;根本無從下手。 所…

C語言從來都沒有過時,你大爺終究是你大爺

直到今天&#xff0c;有人在喊C語言過時的語言&#xff0c;還有什么值得學習的&#xff0c;現在看Python&#xff0c;PHP等語言現在都很容易用&#xff0c;誰還在學習老C語言&#xff0c;其實這是真的嗎&#xff1f;作者下載了兩種語言的源代碼作為下載器。由于空間的限制&…

C語言超級瑪麗菜單模塊源碼

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和…

C語言使用函數必須知道的3點注意事項!

C語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的 C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得…

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/C++的轉義字符詳解

所有的ASCII碼都可以用“\”加數字&#xff08;一般是8進制數字&#xff09;來表示。而C中定義了一些字母前加"\"來表示常見的那些不能顯示的ASCII字符&#xff0c;如\0,\t,\n等&#xff0c;就稱為轉義字符&#xff0c;因為后面的字符&#xff0c;都不是它本來的ASCI…

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語言是面向過程的&#xff0c;而C&#xff0b;&#xff0b;是面向對象的C和C的區別&#xff1a; C是一個結構化語言&#xff0c;它的重點在于算法和數據結構。C程序的設計首要考慮的是如何通過一個過程&#xff0c;對輸入&#xff08;或環境條件&#xff09;進行運算處理得到…

【C語言簡介】C語言的前世今生

C語言的發展歷史&#xff1a; 20世紀70年代初&#xff0c;貝爾實驗室的Dennis Richie 等人在B語言基礎上開發出C語言&#xff0c;最初是作為UNIX的開發語言&#xff1b; 20世紀70年代末&#xff0c;隨著微型計算機的發展&#xff0c;C語言開始移植到非UNIX環境中&#xff0c;并…

C語言/C++編程學習:不找C/C++的工作也要學C/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;虛繼承表現和原理進行一些簡單分析&#xff0c;有不對的地方請指出。下面都是以VC2008編譯器對這兩種機制內部實現為例。 有喜歡或者想學習C/C的朋友加一下我的C/C交流群815393895。謝謝大家的支持 虛函數 以下是百度百科對于虛函數的解釋&a…