download:开课吧 高薪webGL工程师2022最新含源码ppt

编程猿自学it java python go c
基于springboot的国际化解决方案
1底漆
1.1国际化概述
作为一名服务器端开发人员,这里我想从本人的角度简略概述一下国际化(国际化也叫i18n,因为I和N之间有18个字母)是做什么的:

在理论的国际化我的项目中,责任是让服务器依据客户指定的语言,返回相应语言的内容。

1.2 spring/spring boot我的项目中国际化游戏性概述
在spring/springboot的世界中,国内游戏基于以下界面:
org . spring framework . context . message source .
该接口次要定义了以下三种办法:
公共接口音讯源{
//如果国际化配置文件中找不到var1对应的音讯,能够给它一个默认值var3。
@Nullable
String getMessage(String var1,@Nullable Object[] var2,@Nullable String var3,Locale var 4);
//如果在国际化配置文件中找不到var1对应的音讯,抛出异样
String getMessage(String var1,@Nullable Object[] var2,Locale var3)抛出NoSuchMessageException
//这个办法临时没有做太多的钻研,所以本文也不会波及。
string getMessage(MessageSourceResolvable var 1,Locale var2)抛出NoSuchMessageException
}
复制代码
该接口的三个重要实现类如下:

ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
动态音讯源

2如何播放ResourceBundleMessage源(默认)
首先咱们来看看springboot我的项目中国际化最根本的玩法:

(1)构建springboot我的项目(至多增加web依赖)。
Messages.properties(默认配置)

用户名=溜溜球
复制代码

messages_en_US .属性

user.name=yoyo-EN
user.name1=nrsc
user.name2=nrsc{0}-{1}
复制代码

音讯_zh_CN.properties

用户名1=张耳
User.name2=张耳{0}-{1}
复制代码
(3)创立一个控制器测试类
@RestController
公共类i18d专制控制器{
@主动连线
公有MessageSource messageSource

@GetMapping("/hello ")
公共字符串hello() {
String defaultM = messageSource
。getMessage("user.name ",null,locale context holder . getlocale());
字符串message1 =音讯源
。getMessage("user.name1 ",null,locale context holder . getlocale());
字符串message2 = messageSource
。getMessage("user.name2 ",new String[]{"WW "," MM"},locale context holder . getlocale());
字符串message3 = messageSource
。getMessage("user.nameXX ",null," defaultName ",locale context holder . getlocale());

返回default m+" "+message 1+"-"+message 2+" # # "+message 3;
}
}
复制代码
小贴士:

①之所以能够通过@Autowired间接从spring容器中获取MessageSource,是因为如果没有配置这种类型的bean,spring容器会默认初始化一个这种类型的bean——ResourceBundleMessage Source,并放入spring容器中。答案在上面的源文件中:
org . spring framework . boot . auto configure . context . messagesource auto configuration .

②LocaleContextHolder.getLocale()能够获取该申请头中Accept-Language对应的语言环境。

3如何播放ReloadableResourceBundleMessage源码
ReloadableResourceBundleMessageSource和ResourceBundleMessageSource之间最重要的区别在于

前者能够读取位于。属性和。xml,而后者只能读取。属性。
前者能够指定映射文件缓存在内存中的工夫,而后者不能。

ReloadableResourceBundleMessage源码具体玩法如下:

(2)创立ReloadableResourceBundleMessage源,并将其注入到spring容器中。代码如下:
@ Bean(" reloadableResourceBundleMessageSource ")
public message source initReloadableResourceBundleMessageSource(){
ReloadableResourceBundleMessageSource message source = new ReloadableResourceBundleMessageSource();
//指定读取国际化配置文件的根本名称。
message source . set basename(resource utils。class path URL PREFIX+" i18n/messages ");
//指定代码
message source . setdefaultencoding(" UTF-8 ");
//指定缓存工夫
message source . setcacheseconds(60);
返回messageSource
}
复制代码
(3)将注入的MessageSource指定为ReloadableResourceBundleMessage Source。
@主动连线
@ Qualifier(" reloadableResourceBundleMessageSource ")
公有MessageSource messageSource
复制代码
(4)测试留给读者。
4如何播放staticmessagesource
ReloadableResourceBundleMessageSource和ResourceBundleMessageSource基于本地文件,而StaticMessageSource绝对简略。它的根本玩法如下:
(1)创立一个StaticMessageSource,同时指定国际化映射内容,而后放入spring容器。代码如下:
/**
*代码和音讯能够来自数据库或任何其余文件系统。

  • @返回
    */
    @Bean("staticMessageSource ")
    public message source initStaticMessageSource(){
    static message source message source = new static message source();

message source . add message(" user . name ",区域设置。美国,“yoyo-EN”);
message source . add message(" user . name 1 ",区域设置。美国,“nrsc”);
message source . add message(" user . name 2 ",区域设置。美国,“nrsc { 0 }-{ 1 }”;

message source . add message(" user . name ",locale.china,"张耳");
message source . add message(" user . name 1 ",locale.china," Zhang er 1 ");
message source . add message(" user . name 2 ",locale.china,"张耳{ 0 }-{ 1 } ");
返回messageSource
}
复制代码
(2)将注入的音讯源指定为StaticMessageSource。
@主动连线
//@限定符(" reloadableResourceBundleMessageSource ")
@ Qualifier(" static message source ")
公有MessageSource messageSource
复制代码
(3)测试留给读者。
5 DIY
5.1为什么要DIY?
首先,假如你的我的项目国际化实施方案中有以下两个技术需要:

须要翻译的内容很多,须要结构化的存储和治理(比方存储在mysql数据库中)。
心愿你能用redis做缓存。

这个时候你会发现spring/springboot提供的以上三款游戏如同都不行。这个时候,咱们就不得不思考DIY了。
5.2从StaticMessageSource源代码中寻找DIY灵感
依据我的教训,做DIY最好的办法就是模拟和革新源代码。下面介绍的三种游戏中,StaticMessageSource对应的游戏应该是最简略的,也是最好动手的。上面简略看一下它的源代码:
公共类StaticMessageSource扩大了AbstractMessageSource {

/*从“代码+区域设置”键映射到音讯字符串。/
//保留密钥和音讯的映射[密钥格局示例:user.name1_zh_CN]
公有最终映射音讯= new HashMap();
//保留键和音讯格局的映射
//当音讯中有占位符时,将应用此映射(如下面示例中的nrsc{0}-{1})
private final Map cachedMessageFormats = new HashMap();

//给定代码和地区(即地区),从音讯图中取出相应语言的音讯。
@笼罩
受爱护的字符串resolveCodeWithoutArguments(字符串代码,区域设置区域设置){
返回this . messages . get(code+' _ '+locale . tostring());
}
//给定的代码和区域设置(即区域设置)来自cachedMessageFormats映射
//取出对应语言的MessageFormat,父类会联合占位符等信息,解析失去具体的音讯。
@笼罩
@Nullable
受爱护的音讯格局resolveCode(字符串代码,区域设置){
string key = code+' _ '+locale . tostring();
string msg = this . messages . get(key);
if (msg == null) {
返回null
}
//这里采纳的是懒加载形式。首先,cachedMessageFormats的映射是空的。
//调用时,依据音讯和本地生成音讯格局
//而后将生成的MessageFormat放入cachedMessageFormats的映射中。
synchronized(this . cachedmessageformats){
message format message format = this . cachedmessageformats . get(key);
if (messageFormat == null) {
message format = create message format(msg,locale);
this . cachedmessageformats . put(key,message format);
}
返回音讯格局;
}
}

/**
*将给定的音讯与给定的代码相关联。

  • @param code查找代码
  • @param locale应该在其中找到音讯的区域设置
  • @param msg与此查找代码相关联的音讯
    */
    //增加音讯
    public void addMessage(字符串代码、区域设置、字符串音讯){
    Assert.notNull(code,“代码不得为空”);
    Assert.notNull(区域设置,“区域设置不得为空”);
    Assert.notNull(msg,“音讯不得为空”);
    this . messages . put(code+' _ '+locale . tostring()、msg);
    if (logger.isDebugEnabled()) {
    logger.debug("为代码[" + code + "]和区域设置[" + locale + "]")增加了音讯["+msg+"]";
    }
    }

/**
*将给定的音讯值与给定的密钥关联为代码。

  • @param messages要注册的音讯,带有音讯代码
    *作为键,音讯文本作为值
  • @param locale应该在其中找到音讯的区域设置
    */
    //批量增加音讯
    public void addMessages(地图音讯,区域设置){
    Assert.notNull(messages,“音讯映射不能为空”);
    messages.forEach((code,msg) -> addMessage(code,locale,msg));
    }

@笼罩
公共字符串toString() {
返回getClass()。getName()+":"+this . messages;
}
}
复制代码
从下面的源代码来看,其实很简略。
5.3用redis做DIY缓存
上面是一个简略的用redis进行缓存的DIY计划。
(1)在redis中存储数据
@主动连线
公有RedisTemplate redisTemplate

@测试
public void initData() {
List messageInfos = Arrays.asList(
新的MessageInfo("user.name ",区域设置。US.toString()," yoyo-EN "),
新的MessageInfo("user.name1 ",区域设置。US.toString()," nrsc "),
新的MessageInfo("user.name2 ",区域设置。US.toString()," nrsc{0}-{1} ",
音讯信息("用户名",locale.china.tostring(),"张耳"),
音讯信息(" user.name1 ",locale.china.tostring()," Zhanger1 "),
音讯信息(" user.name2 ",locale.china.tostring(),"张耳{0}-{1} ")
);
redisTemplate.opsForValue()。set("userInfo ",message infos);
}
复制代码
(2)模拟动态音讯源定制音讯源
@Component("myMessageSource ")
公共类MyMessageSource扩大了AbstractMessageSource {

private final Map cachedMessageFormats = new HashMap();

@主动连线
公有RedisTemplate redisTemplate

@笼罩
受爱护的字符串resolveCodeWithoutArguments(字符串代码,区域设置区域设置){
map map = getMessagesMap();
返回map . get(code+' _ '+locale . tostring());
}

公有地图getMessagesMap() {
对象userInfoList = redis template . ops for value()。get(" userInfo ");
List message infolist =(List)userInfoList;
map map = new HashMap();
for(音讯信息音讯信息:音讯信息列表){
string key = message info . get code()+' _ '+message info . get locale();
map . computeifaxine(key,k-> message info . getmessage());
}
返回地图;
}

@笼罩
@Nullable
受爱护的音讯格局resolveCode(字符串代码,区域设置){
string key = code+' _ '+locale . tostring();
String msg = getMessagesMap()。get(键);
if (msg == null) {
返回null
}
synchronized(this . cachedmessageformats){
message format message format = this . cachedmessageformats . get(key);
if (messageFormat == null) {
message format = create message format(msg,locale);
this . cachedmessageformats . put(key,message format);
}
返回音讯格局;
}
}
}
复制代码
(3)将注入的MessageSource指定为我的自定义MyMessageSource。
@主动连线
//@限定符(" reloadableResourceBundleMessageSource ")
//@限定符(" staticMessageSource ")
@限定符(" myMessageSource ")
公有MessageSource messageSource
复制代码
(4)测试留给读者。