【SpringBoot框架篇】33.优雅集成i18n实现国际化信息返回

文章目录

  • 1.简介
  • 2.MessageSource配置和工具类封装
    • 2.1.配置MessageSource相关配置
    • 2.2.配置工具类
    • 2.3.测试返回国际级文本信息
  • 3.不优雅的web调用示例(看看就行,别用)
  • 4.优雅使用示例
    • 4.1.错误响应消息枚举类
    • 4.2.ThreadLocal工具类配置
      • 4.2.1.ThreadLocal工具类数据封装
      • 4.2.2.往ThreadLocal里存放language字段
      • 4.2.3.ThreadLocal内存释放
    • 4.3.http返回通用Json响应数据类
    • 4.4.优化章节3的示例
  • 5.各国的国际化文件规范命名列表
  • 6.项目配套代码

1.简介

由于公司业务需求,需要支持中英两个版本的返回提示信息,使用spring的MessageSource类获取对应语种的i18n文件的提示信息。

i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。通常与i18n相关的还有L10n(“本地化”的简称)。

2.MessageSource配置和工具类封装

为节省代码量使用了lombok插件,在pom.xml引入

        <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

查看MessageSource源码可以看到该接口提供了三个方法,我们使用第二个即可。

public interface MessageSource {@NullableString getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4);/*** 根据key去国际化对应的文件中找文本信息* @param var1   key* @param var2  占位符参数* @param var3  国际化语种* @return 对应语种的文本信息*/String getMessage(String var1, @Nullable Object[] var2, Locale var3) throws NoSuchMessageException;String getMessage(MessageSourceResolvable var1, Locale var2) throws NoSuchMessageException;
}

2.1.配置MessageSource相关配置

@Configuration
public class LocaleConfig {@Beanpublic ResourceBundleMessageSource messageSource() {Locale.setDefault(Locale.CHINA);ResourceBundleMessageSource source = new ResourceBundleMessageSource();//设置国际化文件存储路径和名称    i18n目录,messages文件名source.setBasenames("i18n/messages");//设置根据key如果没有获取到对应的文本信息,则返回key作为信息source.setUseCodeAsDefaultMessage(true);//设置字符编码source.setDefaultEncoding("UTF-8");return source;}}

在resources目录下添加国际化文件
en_US存放英语,zh_CH存放中文
在这里插入图片描述
记得把idea的文件编码格式改成utf8,要不然中文会乱码。
在这里插入图片描述

在代码里用枚举类维护使用的语种

@Getter
@AllArgsConstructor
public enum LanguageEnum {ZH_CN("zh_CN", "中文/中国"),EN_US( "en_US", "英语/美国"),;/*** 语言_国家缩写*/private String name;/*** 描述*/private String desc;}

2.2.配置工具类

下面代码封装了两个对外暴露的方法

  • get(String key, String language) 根据key获取文本信息
  • get(String key, Object[] params, String language) 根据key获取替换占位符中的文本信息

MessageSource的实例对象这是通过内部类懒加载的方式创建

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MessageUtil {/*** 根据key信息获取对应语言的内容** @param key      消息key值* @param language 语言_国家* @return*/public static String get(String key, String language) {if (!StringUtils.isEmpty(language)) {String[] arrs = language.split("_");if (arrs.length == 2) {return get(key, new Locale(arrs[0], arrs[1]));}}//使用默认的国际化语言return get(key, Locale.getDefault());}/*** 根据key信息获取对应语言的内容** @param key      消息key值* @param params   需要替换到占位符中的参数 占位符下标重0开始  格式如: {0} {1}* @param language 语言_国家* @return*/public static String get(String key, Object[] params, String language) {if (!StringUtils.isEmpty(language)) {String arrs[] = language.split("_");if (arrs.length == 2) {return get(key, params, new Locale(arrs[0], arrs[1]));}}return get(key, params, Locale.getDefault());}private static String get(String key, Locale language) {return get(key, new String[0], language);}private static String get(String key, Object[] params, Locale language) {return getInstance().getMessage(key, params, language);}private static MessageSource getInstance() {return Lazy.messageSource;}/*** 使用懒加载方式实例化messageSource国际化工具*/private static class Lazy {private static final MessageSource messageSource = SpringUtil.getBean(MessageSource.class);}}

MessageUtil 用到的工具类代码如下

/*** @author Dominick Li* @description 普通类调用Spring bean对象使用的工具栏**/
@Component
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SpringUtil implements ApplicationContextAware {private static ApplicationContext applicationContext = null;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {if (SpringUtil.applicationContext == null) {SpringUtil.applicationContext = applicationContext;}}/*** 通过class获取Bean.*/public static <T> T getBean(Class<T> clazz) {return applicationContext.getBean(clazz);}}

2.3.测试返回国际级文本信息

分别在国际化文件中添加key名称为test的项,并添加{0} 占位符支持动态参数

messages_en_US.properties文件内容如下

test=language:{0} ,Hello World!

messages_zh_CN.properties内容如下

test=语言:{0} ,你好世界

测试代码如下

@RestController
public class TestI18nController {/*** 测试国际化* en_US 英文  http://127.0.0.1:8033/en_US* zh_CN 中文 http://127.0.0.1:8033/zh_CN*/@GetMapping("/{language}")public String test(@PathVariable String language) {String text1 = MessageUtil.get("test", language);String text2 = MessageUtil.get("test", new Object[]{language}, language);return new StringBuilder("没有替换占位符参数:").append(text1).append("<br/>").append("替换占位符参数后:").append(text2).toString();}
}    

用浏览器访问http://127.0.0.1:8033/zh_CN 查看中文国际化信息,访问http://127.0.0.1:8033/en_US 查看英文国际化信息
在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/65abad9a30fc456493f992b1ed05d2d0.png

3.不优雅的web调用示例(看看就行,别用)

像一些固定的参数我们通常会放在Header参数里,例如token,同理我们也可以把language字段放到header里,用于接受客户端选择使用的国际化语言。

Controller和Service的代码如下

@RestController
public class TestI18nController {@Autowiredprivate LoginService loginService;/*** 不优雅登录*/@PostMapping("/login")public JsonResult login(@RequestHeader String language,@RequestParam String username, @RequestParam String password) {return loginService.login(language,username,password);}
}    
public interface LoginService {JsonResult login(String language,String username,String password); 
}
@Service
public class LoginServiceImpl implements LoginService {@Overridepublic JsonResult login(String language, String username, String password) {if ("123456".equals(password)) {//通过key和语言找到对应文本的提示信息return JsonResult.successResult(MessageUtil.get("return.loginOk", language));}return JsonResult.failureResult(MessageUtil.get("return.pwd_error", language));}
}    

在postman中调用测试接口
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
把Header参数中的language改成zh_CN
在这里插入图片描述

上述代码的问题在于

  • 1.国际化使用的language字段需要从controller传递到具体的service类中方法中
  • 2.在每次通过JsonResult返回提示信息的时候都需要显示调用MessageUtil获取对应文本信息

4.优雅使用示例

  • 1.章节3的language字段上下文传递的问题可以通过ThreadLocal的,同理token字段也可以放在ThreadLocal里。
  • 2.显示调用MessageUtil工具类获取提示信息可以封装成响应消息枚举类中使用。

4.1.错误响应消息枚举类

@Getter
public enum ReturnMessageEnum {OK("ok"),FAILED("failed"),LOGIN_OK("loginOk"),PASSWORD_ERROR("pwd_error"),ACCOUNT_LOCK("account_lock"),;/*** 国际化信息文件里的Key前缀*/private final static String prefix = "return.";/*** 返回的国际化key信息*/private String key;ReturnMessageEnum(String key) {this.key = prefix + key;}@Overridepublic String toString() {return MessageUtil.get(key, ThreadLocalManagerUtil.getLanguage());}/*** 替换占位符中的参数** @param params 需要替换的参数值 长度可变*/public String setAndToString(Object... params) {return MessageUtil.get(key, params, ThreadLocalManagerUtil.getLanguage());}}

国际化文件里也需要添加对应的数据

messages_zh_CN.properties文件

return.ok=操作成功!
return.failed=操作失败!
return.loginOk=登录成功!
return.pwd_error=密码错误!
return.account_lock=账号已被锁定{0}分钟,请稍后重试!

messages_en_US.properties

return.ok=Success!
return.failed=Fail!
return.loginOk=Login succeeded!
return.pwd_error=PW error!
return.account_lock=The account has been locked for {0} minutes. Please try again later!

4.2.ThreadLocal工具类配置

系统参数常量类

/*** 系统常量*/
public interface SystemConstants {/*** 国际化*/String LANGUAGE = "language";/*** token名称*/String TOKEN_NAME = "token";}

4.2.1.ThreadLocal工具类数据封装

public class ThreadLocalManagerUtil {@Datapublic static class HeaderInfo {/*** 用户的token信息*/private String token;/*** 国际化语言包名称*/private String language;}/*** 存储请求头信息*/private final static ThreadLocal<HeaderInfo> headerInfoThreadLocal = new ThreadLocal<>();public static void add(HeaderInfo headerInfo) {headerInfoThreadLocal.set(headerInfo);}public static String getToken() {return headerInfoThreadLocal.get().getToken();}public static String getLanguage() {return headerInfoThreadLocal.get() != null ? headerInfoThreadLocal.get().getLanguage() : LanguageEnum.ZH_CN.getName();}/*** 释放资源*/public static void remove() {headerInfoThreadLocal.remove();}}

4.2.2.往ThreadLocal里存放language字段

使用过空滤器拦截接口请求,并从请求头中获取language字段存放到ThreadLocal中

@Component
public class HttpFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;ThreadLocalManagerUtil.HeaderInfo headerInfo  = new ThreadLocalManagerUtil.HeaderInfo();headerInfo.setLanguage(request.getHeader(SystemConstants.LANGUAGE));headerInfo.setToken(request.getHeader(SystemConstants.TOKEN_NAME));//在ThreadLocal中添加token和当前国际化信息ThreadLocalManagerUtil.add(headerInfo);filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {}}

4.2.3.ThreadLocal内存释放

如果ThreadLocal存放的数据不释放,当用户量大的时候会导致系统出现OOM问题

添加拦截器在请求结束的时候释放内存。

public class HttpInterceptor  implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//会话结束移除线程缓存ThreadLocalManagerUtil.remove();return;}
}

注册拦截器到webMVC中

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry interceptor) {//需要配置拦截器并指定拦截的接口路径interceptor.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");}
}

4.3.http返回通用Json响应数据类

public class JsonResult<T> implements Serializable {/*** 成功标识 200成功,其它异常*/private int code = 200;/*** 提示信息*/private String msg;/*** 数据*/@JsonInclude(JsonInclude.Include.NON_NULL)private T data;private static final long serialVersionUID = -7268040542410707954L;public JsonResult() {}public JsonResult(int code) {this.setCode(code);}public JsonResult(int code, String msg) {this(code);this.setMsg(msg);}public JsonResult(int code, String msg, T data) {this(code, msg);this.setData(data);}public static JsonResult build(boolean flag) {return new JsonResult(flag ? HttpStatus.OK.value() : HttpStatus.INTERNAL_SERVER_ERROR.value(), flag ? ReturnMessageEnum.OK.toString() : ReturnMessageEnum.FAILED.toString());}public static JsonResult successResult() {return new JsonResult(HttpStatus.OK.value(), ReturnMessageEnum.OK.toString());}public static JsonResult successResult(String msg) {return new JsonResult(HttpStatus.OK.value(), defaultSuccessMsg(msg));}public static <T> JsonResult<T> successResult(T obj) {return new JsonResult(HttpStatus.OK.value(), ReturnMessageEnum.OK.toString(), obj);}public static <T> JsonResult<T> successResult(String msg, T obj) {return new JsonResult(HttpStatus.OK.value(), defaultSuccessMsg(msg), obj);}public static JsonResult failureResult() {return new JsonResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), ReturnMessageEnum.FAILED.toString());}public static JsonResult failureResult(String msg) {return new JsonResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), defautlErrorMsg(msg));}public static JsonResult failureResult(ReturnMessageEnum returnMessageEnum) {return new JsonResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), returnMessageEnum.toString());}protected static String defautlErrorMsg(String msg) {if (!StringUtils.isEmpty(msg)) {return msg;} else {return ReturnMessageEnum.FAILED.toString();}}protected static String defaultSuccessMsg(String msg) {if (!StringUtils.isEmpty(msg)) {return msg;} else {return ReturnMessageEnum.OK.toString();}}
}

4.4.优化章节3的示例

@RestController
public class TestI18nController {@Autowiredprivate LoginService loginService;/*** 优化后的登录接口*/@PostMapping("/login")public JsonResult login(@RequestParam String username, @RequestParam String password) {return loginService.login(username,password);}
}    
public interface LoginService {JsonResult login(String username,String password); 
}
@Service
public class LoginServiceImpl implements LoginService {@Overridepublic JsonResult login(String username, String password) {if ("123456".equals(password)) {return JsonResult.successResult(ReturnMessageEnum.LOGIN_OK);}//使用枚举替换占位符内容后的信息System.out.println(ReturnMessageEnum.ACCOUNT_LOCK.setAndToString(3));return JsonResult.failureResult(ReturnMessageEnum.PASSWORD_ERROR);}
}   

使用postman调用接口测试可以实现同章节3一样的效果。
在这里插入图片描述
改成英语后调用也是一样的。
在这里插入图片描述

使用枚举类替换占位符内容后的信息也打印在控制台上面了。
在这里插入图片描述

5.各国的国际化文件规范命名列表

命名规则 语种_地区,中国,香港,台湾都属于中国,所以前面都是zh开头,_后面则表示地区

中国 : zh_CN
香港 : zh_HK
台湾:zh_TW
日本 : ja_JP
美国 : en_US
英国 : en_GB
秘鲁 : es_PE
巴拿马 : es_PA
波斯尼亚和黑山共和国 : sr_BA
危地马拉 : es_GT
阿拉伯联合酋长国 : ar_AE
挪威 : no_NO
阿尔巴尼亚 : sq_AL
伊拉克 : ar_IQ
也门 : ar_YE
葡萄牙 : pt_PT
塞浦路斯 : el_CY
卡塔尔 : ar_QA
马其顿王国 : mk_MK
瑞士 : de_CH
芬兰 : fi_FI
马耳他 : en_MT
斯洛文尼亚 : sl_SI
斯洛伐克 : sk_SK
土耳其 : tr_TR
沙特阿拉伯 : ar_SA
塞尔维亚及黑山 : sr_CS
新西兰 : en_NZ
挪威 : no_NO
立陶宛 : lt_LT
尼加拉瓜 : es_NI
爱尔兰 : ga_IE
比利时 : fr_BE
西班牙 : es_ES
黎巴嫩 : ar_LB
加拿大 : fr_CA
爱沙尼亚 : et_EE
科威特 : ar_KW
塞尔维亚 : sr_RS
墨西哥 : es_MX
苏丹 : ar_SD
印度尼西亚 : in_ID
乌拉圭 : es_UY
拉脱维亚 : lv_LV
巴西 : pt_BR
叙利亚 : ar_SY
多米尼加共和国 : es_DO
瑞士 : fr_CH
印度 : hi_IN
委内瑞拉 : es_VE
巴林 : ar_BH
菲律宾 : en_PH
突尼斯 : ar_TN
奥地利 : de_AT
荷兰 : nl_NL
厄瓜多尔 : es_EC
约旦 : ar_JO
冰岛 : is_IS
哥伦比亚 : es_CO
哥斯达黎加 : es_CR
智利 : es_CL
埃及 : ar_EG
南非 : en_ZA
泰国 : th_TH
希腊 : el_GR
意大利 : it_IT
匈牙利 : hu_HU
爱尔兰 : en_IE
乌克兰 : uk_UA
波兰 : pl_PL
卢森堡 : fr_LU
比利时 : nl_BE
印度 : en_IN
西班牙 : ca_ES
摩洛哥 : ar_MA
玻利维亚 : es_BO
澳大利亚 : en_AU
新加坡 : zh_SG
萨尔瓦多 : es_SV
俄罗斯 : ru_RU
韩国 : ko_KR
阿尔及利亚 : ar_DZ
越南 : vi_VN
黑山 : sr_ME
利比亚 : ar_LY
白俄罗斯 : be_BY
以色列 : iw_IL
保加利亚 : bg_BG
马耳他 : mt_MT
巴拉圭 : es_PY
法国 : fr_FR
捷克共和国 : cs_CZ
瑞士 : it_CH
罗马尼亚 : ro_RO
波多黎哥 : es_PR
加拿大 : en_CA
德国 : de_DE
卢森堡 : de_LU
阿根廷 : es_AR
马来西亚 : ms_MY
克罗地亚 : hr_HR
新加坡 : en_SG
阿曼 : ar_OM
泰国 : th_TH
瑞典 : sv_SE
丹麦 : da_DK
洪都拉斯 : es_HN

由于前段时间买相机学习摄影相关的知识,导致博客很久没更新了,还是不能忘记初心,要时刻学习,在这个社会一日不学习就会退步,因为会被同行卷下去。。

6.项目配套代码

github地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在github上帮我点下 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

  • 1.搭建第一个springboot项目
  • 2.Thymeleaf模板引擎实战
  • 3.优化代码,让代码更简洁高效
  • 4.集成jta-atomikos实现分布式事务
  • 5.分布式锁的实现方式
  • 6.docker部署,并挂载配置文件到宿主机上面
  • 7.项目发布到生产环境
  • 8.搭建自己的spring-boot-starter
  • 9.dubbo入门实战
  • 10.API接口限流实战
  • 11.Spring Data Jpa实战
  • 12.使用druid的monitor工具查看sql执行性能
  • 13.使用springboot admin对springboot应用进行监控
  • 14.mybatis-plus实战
  • 15.使用shiro对web应用进行权限认证
  • 16.security整合jwt实现对前后端分离的项目进行权限认证
  • 17.使用swagger2生成RESTful风格的接口文档
  • 18.使用Netty加websocket实现在线聊天功能
  • 19.使用spring-session加redis来实现session共享
  • 20.自定义@Configuration配置类启用开关
  • 21.对springboot框架编译后的jar文件瘦身
  • 22.集成RocketMQ实现消息发布和订阅
  • 23.集成smart-doc插件零侵入自动生成RESTful格式API文档
  • 24.集成FastDFS实现文件的分布式存储
  • 25.集成Minio实现文件的私有化对象存储
  • 26.集成spring-boot-starter-validation对接口参数校验
  • 27.集成mail实现邮件推送带网页样式的消息
  • 28.使用JdbcTemplate操作数据库
  • 29.Jpa+vue实现单模型的低代码平台
  • 30.使用sharding-jdbc实现读写分离和分库分表
  • 31.基于分布式锁或xxl-job实现分布式任务调度
  • 32.基于注解+redis实现表单防重复提交
  • 33.优雅集成i18n实现国际化信息返回

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

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

相关文章

(四)Node.js - npm与包

1. 什么是包 Node.js中的第三方模块又叫做包。 不同于Node.js中的内置模块与自定义模块&#xff0c;包是由第三方个人或团队开发出来的&#xff0c;免费供所有人使用。 由于Node.js的内置模块进提供了一些底层的API&#xff0c;导致在基于内置模块进行项目开发时&#xff0c…

USB(二):Type-C

一、引脚定义 Type-C口有 4对TX/RX差分线&#xff0c;2对USB D/D-&#xff0c;1对SBU&#xff0c;2个CC&#xff0c;4个VBUS和4个地线Type-C母座视图&#xff1a; Type-C公头视图&#xff1a; 二、关键名词 DFP(Downstream Facing Port)&#xff1a; 下行端口&#xff0c…

Direct path read LOB

Table full scan &#xff1a; wait event Direct path read because of LOB "Direct path read" Wait Event During LOB Access (Doc ID 2287482.1)​编辑To Bottom In this Document Symptoms Changes Cause Solution References APPLIES TO: Oracle Database …

win11虚拟机安装

win11虚拟机安装 下载虚拟机客户端安装客户端创建虚拟机下载 ISO切换root账号GNOME桌面 下载虚拟机客户端 版本是16.2.3 链接&#xff1a;https://pan.baidu.com/s/13c6XVWFbeQKbCnrlfxD8cA 提取码&#xff1a;qxdc 安装客户端 安装向导 点击下一步 接收条款&#xff0c;点…

爬虫012_字典高级操作_查询_修改_添加_删除和清空_遍历---python工作笔记031

然后来看字典高级,首先 打印某个元素 然后打印的时候注意,如果直接打印的值,在字典中没有就报错 这里要注意不能用点访问

【多维定向滤波器组和表面波】表面变换:用于高效表示多维 s 的多分辨率变换(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Unity 基础函数

Mathf&#xff1a; //1.π-PI print(Mathf.PI); //2.取绝对值-Abs print(Mathf.Abs(-10)); print(Mathf.Abs(-20)); print(Mathf.Abs(1)); //3.向上取整-Ce il To In t float f 1.3f; int i (int)f; …

如何在群辉NAS系统下安装cpolar套件,并使用cpolar内网穿透?

如何在群辉NAS系统下安装cpolar套件,并使用cpolar内网穿透&#xff1f; 文章目录 如何在群辉NAS系统下安装cpolar套件,并使用cpolar内网穿透&#xff1f;前言1. 在群辉NAS系统下安装cpolar套件2. 管理隧道列表3. 创建固定数据隧道 前言 群晖作为大容量存储系统&#xff0c;既可…

bootloader跳转APP注意事项

在gd32f427 时跳转异常 参考文章&#xff1a; https://club.rt-thread.org/ask/question/425321.html%20https:/club.rt-thread.org/ask/question/eab19452583b5959.html https://club.rt-thread.org/ask/question/eab19452583b5959.html 关闭全部中断&#xff0c;并且清除中…

springcloud3 springcloud stream的学习以及案例

一 springcloud stream的作用 1.1 springcloud stream作用 stream屏蔽底层消息中间件的差异&#xff0c;降低切换成本&#xff0c;统一消息的编程模型。 stream中的消息通信模式遵循了“发布-订阅”模式。 1.2 Binder作用 通过定义绑定器Binder作为中间层&#xff0c;实现…

力扣hot100刷题记录

二刷hot100&#xff0c;坚持每天打卡&#xff01;&#xff01;&#xff01;Today&#xff1a;2023-8-10 1. 两数之和 // 先求差&#xff0c;再查哈希表 public int[] twoSum(int[] nums, int target) {Map<Integer,Integer> map new HashMap<>();for(int i 0;i&…

SpringBoot 升级内嵌Tomcat

SpringBoot 更新 Tomcat 最近公司的一个老项目需要升级下Tomcat&#xff0c;由于这个项目我完全没有参与&#xff0c;所以一开始我以为是一个老的Tomcat项目&#xff0c;升级它的Tomcat依赖或者是Tomcat容器镜像&#xff0c;后面发现是一个SpringBoot项目&#xff0c;升级的是…

Dockerfile 简单实战

将flask项目打包成镜像 1. 准备flask文件 创建 app.py 文件&#xff0c;内容如下 from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello Worldif __name__ __main__:app.run(host0.0.0.0, port8000, debugTrue) 并开启外网访问&#xf…

vue消息订阅与发布,实现任意组件间通讯

第一步&#xff1a;下载第三方消息订阅与发布库&#xff0c;例如常用的pubsub.js,他可以在任何框架中使用包括vue、react、anglar等等。 命令&#xff1a;npm i pubsub-js 注意是pubsub-js(不是点); 第二步&#xff1a;引入库&#xff1b; import pubsub from pubsub-js 第…

Java并发编程(二)并发理论[JMM/重排序/内存屏障/Happens-Before 规则]

JMM(Java内存模型) 概述 JMM即Java内存模型(Java Memory Model),是一种抽象的概念,并不真实存在,JMM描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操…

Android AOSP源码编译——AOSP下载(一)

一、电脑配置 Ubuntu16.04 16G&#xff0c;硬盘的大小最好大于300G (我这边是找了个win电脑装了双系统 没有使用虚拟机的方式) 二、基础环境配置 1、安装git sudo apt install git配置git email和name git config --global user.email "youexample.com" git conf…

数据结构—树和二叉树

5.树和二叉树 5.1树和二叉树的定义 树形结构&#xff08;非线性结构&#xff09;&#xff1a;结点之间有分支&#xff0c;具有层次关系。 5.1.1树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。 若n0&#xff0c;称为空树&#x…

LVS工作环境配置

一、LVS-DR工作模式配置 模拟环境如下&#xff1a; 1台客户机 1台LVS负载调度器 2台web服务器 1、环境部署 &#xff08;1&#xff09;LVS负载调度器 yum install -y ipvsadm # 在LVS负载调度器上进行环境安装 ifconfig ens33:200 192.168.134.200/24 # 配置LVS的VIP…

Java自学网站推荐,专业教学快速提升

Java自学网站可以是学习Java的有用资源之一。它们通常提供了丰富的教学材料、在线课程、编程练习和实例项目&#xff0c;帮助初学者系统地学习Java编程语言和相关技术。 动力节点是一家专业的Java培训机构&#xff0c;他们提供在线视频学习平台&#xff0c;你可以参考他们的官方…

数据结构----结构--线性结构--链式存储--链表

数据结构----结构–线性结构–链式存储–链表 1.链表的特点 空间可以不连续&#xff0c;长度不固定&#xff0c;相对于数组灵活自由 搜索&#xff1a; 时间复杂度O(n) 增删: 头增头删时间复杂度O(1) 其他时间复杂度为O(n) 扩展&#xff1a;单向循环链表的特性 从任意节…