Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
Spring教程
-> 国际化详解
1、Spring是什么?我们为什么要学习它?
2、控制反转(IoC)与依赖注入(DI)
3、Spring容器基本使用及原理
4、xml中bean定义详解
5、容器创建bean实例有多少种?
6、bean作用域scope详解
7、依赖注入之手动注入
8、依赖注入之自动注入(autowire)详解
9、depend-on干什么的?
10、primary可以解决什么问题?
11、bean中的autowire-candidate属性又是干什么的?
12、lazy-init:bean延迟初始化
13、使用继承简化bean配置(abstract & parent)
14、lookup-method和replaced-method比较陌生,怎么玩的?
15、代理详解(java动态代理&CGLIB代理)
16、深入理解java注解(预备知识)
17、@Configration、@Bean注解详解
18、@ComponentScan、@ComponentScans详解
19、@Import 注解详解
20、@Conditional通过条件来控制bean的注册
21、注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)
22、@Scope、@DependsOn、@ImportResource、@Lazy
23、Bean生命周期详解
24、父子容器
25、@PropertySource、@Value注解及动态刷新实现
26、国际化详解
27、事件详解
28、循环bean详解
29、BeanFactory扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
30、jdk动态代理和cglib代理
31、Aop概念详解
32、AOP核心源码、原理详解
33、ProxyFactoryBean创建AOP代理
34、@Aspect中@Pointcut 12种用法
35、@Aspect中5中通知详解
36、@EnableAspectJAutoProxy、@Aspect中通知顺序详解
37、@EnableAsync & @Async 实现方法异步调用
38、@Scheduled & @EnableScheduling定时器详解
39、强大的Spel表达式
40、缓存使用(@EnableCaching、@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig)
41、@EnableCaching集成redis缓存
42、JdbcTemplate实现增删改查如此简单?
43、Spring中编程式事务怎么用的?
44、Spring声明事务怎么用的?
45、详解Spring事务中7种传播行为
46、Spring如何管理多数据源事务?
47、Spring事务源码解析
48、@Transaction 事务源码解析
49、实战篇:手把手带你实现事务消息!
50、Spring事务拦截器顺序如何控制?
51、Spring事务失效常见的几种情况
52、Spring实现数据库读写分离
53、Spring集成MyBatis
54、集成junit
55、Spring上下文生命周期
56、面试官:循环依赖不用三级缓存可以么?
57、Spring常用工具类
上一篇:@PropertySource、@Value注解及动态刷新实现
下一篇:事件详解
<div style="display:none"></div> 上次去一个电商公司面试: 面试官:Spring中国际化这块的东西用过么?可以介绍一下么? 我:spring中对国际化支持挺好的,比较简单,只需要按照语言配置几个properties文件,然后主要注册一个国际化的相关的bean,同时需指定一下配置文件的位置,基本上就可以了 面试官:那如果配置文件内容有变化?你们怎么解决的? 我:这块啊,spring国际化这块有个实现类,可以检测到配置文件的变化,就可以解决你这个问题 面试官:那我们是否可以将这些国际化的配置丢到db中去管理呢? 我:这个地方我没有搞过,基本上我们这边都是将国际化的配置文件放在项目中的properties文件中;不过以我对spring的理解,spring扩展方面是非常优秀的,应该是可以这么做的,自己去实现一下spring国际化相关接口就可以了。 面试官:工资期望多少? 我:2万 面试官:恭喜你,下周来上班! 为了方便大家,准备把这块知识细化一下,方便大家面试及使用。 ## 本次问题 1. Spring中国际化怎么用? 2. 国际化如何处理资源文件变化的问题? 3. 国际化资源配置放在db中如何实现? ## 先说一下什么是国际化 **简单理解,就是对于不同的语言,做出不同的响应。** 比如页面中有个填写用户信息的表单,有个姓名的输入框 浏览器中可以选择语言 选中文的时候会显示: ```java 姓名:一个输入框 ``` 选英文的时候会显示: ```java Full name:一个输入框 ``` 国际化就是做这个事情的,根据不同的语言显示不同的信息。 所以需要支持国际化,得先知道选择的是哪种地区的哪种语言,java中使用`java.util.Locale`来表示地区语言这个对象,内部包含了国家和语言的信息。 Locale中有个比较常用的构造方法 ```java public Locale(String language, String country) { this(language, country, ""); } ``` > 2个参数: > > language:语言 > > country:国家 > > 语言和国家这两个参数的值不是乱写的,国际上有统一的标准: > > 比如language的值:zh表示中文,en表示英语,而中文可能很多地区在用,比如大陆地区可以用:CN,新加坡用:SG;英语也是有很多国家用的,GB表示英国,CA表示加拿大 > > 国家语言简写格式:language-country,如:zh-CN(中文【中国】),zh-SG(中文【新加坡】),en-GB(英语【英国】), > > en-CA(英语【加拿大】)。 > > 还有很多,这里就不细说了,国家语言编码给大家提供一个表格:http://www.itsoku.com/article/282 **Locale类中已经创建好了很多常用的Locale对象,直接可以拿过来用**,随便列几个看一下: ```java static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN"); //zh_CN static public final Locale UK = createConstant("en", "GB"); //en_GB static public final Locale US = createConstant("en", "US"); //en_US static public final Locale CANADA = createConstant("en", "CA"); //en_CA ``` 再回头看前面的问题:页面中显示姓名对应的标签,需要我们根据一个key及Locale信息来获取对应的国际化信息,spring中提供了这部分的实现,下面我们来看详情。 ## Spring中国际化怎么用? ### MessageSource接口 spring中国际化是通过MessageSource这个接口来支持的 ```java org.springframework.context.MessageSource ``` 内部有3个常用的方法用来获取国际化信息,来看一下 ```java public interface MessageSource { /** * 获取国际化信息 * @param code 表示国际化资源中的属性名; * @param args用于传递格式化串占位符所用的运行期参数; * @param defaultMessage 当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息; * @param locale 表示本地化对象 */ @Nullable String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale); /** * 与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常 */ String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException; /** * @param MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个方法相同 */ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException; } ``` ### 常见3个实现类 #### ResourceBundleMessageSource 这个是基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源 #### ReloadableResourceBundleMessageSource 这个功能和第一个类的功能类似,多了定时刷新功能,允许在不重启系统的情况下,更新资源的信息 #### StaticMessageSource 它允许通过编程的方式提供国际化信息,一会我们可以通过这个来实现db中存储国际化信息的功能。 ## Spring中使用国际化的3个步骤 通常我们使用spring的时候,都会使用带有ApplicationContext字样的spring容器,这些容器一般是继承了AbstractApplicationContext接口,而这个接口实现了上面说的国际化接口MessageSource,所以通常我们用到的ApplicationContext类型的容器都自带了国际化的功能。 通常我们在ApplicationContext类型的容器中使用国际化3个步骤 **步骤一:创建国际化文件** **步骤二:向容器中注册一个MessageSource类型的bean,bean名称必须为:messageSource** **步骤三:调用AbstractApplicationContext中的getMessage来获取国际化信息,其内部将交给第二步中注册的messageSource名称的bean进行处理** ### 来个案例感受一下 #### 创建国际化文件 **国际化文件命名格式:名称\_语言\_地区.properties** 我们来3个文件,文件都放在下面这个目录中 ```java com/javacode2018/lesson002/demo19/ ``` ##### message.properties ```properties name=您的姓名 personal_introduction=默认个人介绍:{0},{1} ``` > 这个文件名称没有指定Local信息,当系统找不到的时候会使用这个默认的 ##### message_cn_ZH.properties:中文【中国】 ```properties name=姓名 personal_introduction=个人介绍:{0},{1},{0} ``` ##### message_en_GB.properties:英文【英国】 ```properties name=Full name personal_introduction=personal_introduction:{0},{1},{0} ``` #### spring中注册国际化的bean 注意必须是MessageSource类型的,bean名称必须为messageSource,此处我们就使用ResourceBundleMessageSource这个类 ```java package com.javacode2018.lesson002.test19.demo1; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; @Configuration public class MainConfig1 { @Bean public ResourceBundleMessageSource messageSource() { ResourceBundleMessageSource result = new ResourceBundleMessageSource(); //可以指定国际化化配置文件的位置,格式:路径/文件名称,注意不包含【语言_国家.properties】含这部分 result.setBasenames("com/javacode2018/lesson002/demo19/message"); //@1 return result; } } ``` @1:这个地方的写法需要注意,可以指定国际化化配置文件的位置,格式:路径/文件名称,注意不包含**【语言_国家.properties】**含这部分 #### 来个测试用例 ```java package com.javacode2018.lesson002.test19; import com.javacode2018.lesson002.test19.demo1.MainConfig1; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.Locale; public class MessageSourceTest { @Test public void test1() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig1.class); context.refresh(); //未指定Locale,此时系统会取默认的locale对象,本地默认的值中文【中国】,即:zh_CN System.out.println(context.getMessage("name", null, null)); System.out.println(context.getMessage("name", null, Locale.CHINA)); //CHINA对应:zh_CN System.out.println(context.getMessage("name", null, Locale.UK)); //UK对应en_GB } } ``` #### 运行输出 ```java 您的姓名 您的姓名 Full name ``` > 第一行未指定Locale,此时系统会取默认的locale对象,本地默认的值中文【中国】,即:zh_CN,所以会获取到`message_zh_CN.properties`中的内容。 > > 后面2行,都指定了Locale对象,找到对应的国际化文件,取值。 #### 动态参数使用 注意配置文件中的`personal_introduction`,个人介绍,比较特别,包含了`{0},{1},{0}`这样一部分内容,这个就是动态参数,调用`getMessage`的时候,通过第二个参数传递过去,来看一下用法: ```java @Test public void test2() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig1.class); context.refresh(); //未指定Locale,此时系统会取默认的,本地电脑默认的值中文【中国】,即:zh_CN System.out.println(context.getMessage("personal_introduction", new String[]{"spring高手", "java高手"}, Locale.CHINA)); //CHINA对应:zh_CN System.out.println(context.getMessage("personal_introduction", new String[]{"spring", "java"}, Locale.UK)); //UK对应en_GB } ``` #### 运行输出 ```java 默认个人介绍:spring高手,java高手 personal_introduction:spring,java,spring ``` ## 监控国际化文件的变化 用`ReloadableResourceBundleMessageSource`这个类,功能和上面案例中的`ResourceBundleMessageSource`类似,不过多了个可以监控国际化资源文件变化的功能,有个方法用来设置缓存时间: ```java public void setCacheMillis(long cacheMillis) ``` > -1:表示永远缓存 > > 0:每次获取国际化信息的时候,都会重新读取国际化文件 > > 大于0:上次读取配置文件的时间距离当前时间超过了这个时间,重新读取国际化文件 还有个按秒设置缓存时间的方法`setCacheSeconds`,和`setCacheMillis`类似 下面我们来案例 ```java package com.javacode2018.lesson002.test19.demo2; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; @Configuration public class MainConfig2 { @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource result = new ReloadableResourceBundleMessageSource(); result.setBasenames("com/javacode2018/lesson002/demo19/message"); //设置缓存时间1000毫秒 result.setCacheMillis(1000); return result; } } ``` message_zh_CN.properties中新增一行内容 ```properties address=上海 ``` 对应的测试用例 ```java @Test public void test3() throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig2.class); context.refresh(); //输出2次 for (int i = 0; i < 2; i++) { System.out.println(context.getMessage("address", null, Locale.CHINA)); TimeUnit.SECONDS.sleep(5); } } ``` 上面有个循环,当第一次输出之后,修改一下`message_zh_CN.properties`中的address为`上海松江`,最后运行结果如下: ```java 上海 上海松江 ``` **使用注意:线上环境,缓存时间最好设置大一点,性能会好一些。** ## 国际化信息存在db中 上面我们介绍了一个类:`StaticMessageSource`,这个类它允许通过编程的方式提供国际化信息,我们通过这个类来实现从db中获取国际化信息的功能。 这个类中有2个方法比较重要: ```java public void addMessage(String code, Locale locale, String msg); public void addMessages(Map<String, String> messages, Locale locale); ``` 通过这两个方法来添加国际化配置信息。 下面来看案例 自定义一个StaticMessageSource类 ```java package com.javacode2018.lesson002.test19.demo3; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.support.StaticMessageSource; import java.util.Locale; public class MessageSourceFromDb extends StaticMessageSource implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { //此处我们在当前bean初始化之后,模拟从db中获取国际化信息,然后调用addMessage来配置国际化信息 this.addMessage("desc", Locale.CHINA, "我是从db来的信息"); this.addMessage("desc", Locale.UK, "MessageSource From Db"); } } ``` > 上面的类实现了spring的InitializingBean接口,重写了接口中干掉afterPropertiesSet方法,这个方法会在当前bean初始化之后调用,在这个方法中模拟从db中获取国际化信息,然后调用addMessage来配置国际化信息 来个spring配置类,将MessageSourceFromDb注册到spring容器 ```java package com.javacode2018.lesson002.test19.demo3; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig3 { @Bean public MessageSource messageSource(){ return new MessageSourceFromDb(); } } ``` 上测试用例 ```java @Test public void test4() throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig3.class); context.refresh(); System.out.println(context.getMessage("desc", null, Locale.CHINA)); System.out.println(context.getMessage("desc", null, Locale.UK)); } ``` 运行输出 ```java 我是从db来的信息 MessageSource From Db ``` ## bean名称为什么必须是messageSource 上面我容器启动的时候会调用`refresh`方法,过程如下: ```java org.springframework.context.support.AbstractApplicationContext#refresh 内部会调用 org.springframework.context.support.AbstractApplicationContext#initMessageSource 这个方法用来初始化MessageSource,方法内部会查找当前容器中是否有messageSource名称的bean,如果有就将其作为处理国际化的对象 如果没有找到,此时会注册一个名称为messageSource的MessageSource ``` ## 自定义bean中使用国际化 自定义的bean如果想使用国际化,比较简单,只需实现下面这个接口,spring容器会自动调用这个方法,将`MessageSource`注入,然后我们就可以使用`MessageSource`获取国际化信息了。 ```java public interface MessageSourceAware extends Aware { void setMessageSource(MessageSource messageSource); } ``` ## 总结 **本文介绍了国际化的使用,涉及到了java中的Locale类,这个类用来表示语言国家信息,获取国际化信息的时候需要携带这个参数,spring中通过`MessageSource`接口来支持国际化的功能,有3个常用的实现类需要了解,`StaticMessageSource`支持硬编码的方式配置国际化信息。** **如果需要spring支撑国际化,需要注册一个bean名称为messageSource的MessageSource,这个一定要注意。** 到此,上面面试的3个问题,大家都能轻松应对了。 ## 案例源码 ```java https://gitee.com/javacode2018/spring-series ``` **路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。** <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container