关于java:设计模式-结构型代理模式装饰者模式适配器模式桥接模式门面模式组合模式亨元模式

结构型模式次要总结了一些类或对象组合在一起的经典构造,这些经典的构造能够解决特定利用场景的问题。 罕用的:代理模式、桥接模式、装璜者模式、适配器模式 不罕用的:门面模式、组合模式、亨元模式 一、代理模式它在不扭转原始类(或叫被代理类)代码的状况下,通过引入代理类来给原始类附加性能。 1.1 实现形式代理类和原始类实现雷同的接口 public interface IUserController { UserVo login(String telephone, String password); UserVo register(String telephone, String password);}public class UserController implements IUserController { //...省略其余属性和办法... @Override public UserVo login(String telephone, String password) {   //...省略login逻辑...   //...返回UserVo数据...} @Override public UserVo register(String telephone, String password) {   //...省略register逻辑...   //...返回UserVo数据...}}public class UserControllerProxy implements IUserController { private MetricsCollector metricsCollector; private UserController userController; public UserControllerProxy(UserController userController) {   this.userController = userController;   this.metricsCollector = new MetricsCollector();} @Override public UserVo login(String telephone, String password) {   long startTimestamp = System.currentTimeMillis();   // 委托   UserVo userVo = userController.login(telephone, password);   long endTimeStamp = System.currentTimeMillis();   long responseTime = endTimeStamp - startTimestamp;   RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);   metricsCollector.recordRequest(requestInfo);   return userVo;} @Override public UserVo register(String telephone, String password) {   long startTimestamp = System.currentTimeMillis();   UserVo userVo = userController.register(telephone, password);   long endTimeStamp = System.currentTimeMillis();   long responseTime = endTimeStamp - startTimestamp;   RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);   metricsCollector.recordRequest(requestInfo);   return userVo;}}//UserControllerProxy应用举例//因为原始类和代理类实现雷同的接口,是基于接口而非实现编程//将UserController类对象替换为UserControllerProxy类对象,不须要改变太多代码IUserController userController = new UserControllerProxy(newUserController());继承的形式 public class UserControllerProxy extends UserController { private MetricsCollector metricsCollector; public UserControllerProxy() {   this.metricsCollector = new MetricsCollector();} public UserVo login(String telephone, String password) {   long startTimestamp = System.currentTimeMillis();   UserVo userVo = super.login(telephone, password);   long endTimeStamp = System.currentTimeMillis();   long responseTime = endTimeStamp - startTimestamp;   RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);   metricsCollector.recordRequest(requestInfo);   return userVo;} public UserVo register(String telephone, String password) {   long startTimestamp = System.currentTimeMillis();   UserVo userVo = super.register(telephone, password);   long endTimeStamp = System.currentTimeMillis();   long responseTime = endTimeStamp - startTimestamp;   RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);   metricsCollector.recordRequest(requestInfo);   return userVo;}}//UserControllerProxy应用举例UserController userController = new UserControllerProxy();动静代理 public class MetricsCollectorProxy { private MetricsCollector metricsCollector; public MetricsCollectorProxy() {   this.metricsCollector = new MetricsCollector();} public Object createProxy(Object proxiedObject) {   Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();   DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);   returnProxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);} private class DynamicProxyHandler implements InvocationHandler {   private Object proxiedObject;   public DynamicProxyHandler(Object proxiedObject) {     this.proxiedObject = proxiedObject;  }   @Override   public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {     long startTimestamp = System.currentTimeMillis();     Object result = method.invoke(proxiedObject, args);     long endTimeStamp = System.currentTimeMillis();     long responseTime = endTimeStamp - startTimestamp;     String apiName = proxiedObject.getClass().getName() + ":" +method.getName();     RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp);     metricsCollector.recordRequest(requestInfo);     return result;  }}}//MetricsCollectorProxy应用举例MetricsCollectorProxy proxy = new MetricsCollectorProxy();IUserController userController = (IUserController) proxy.createProxy(newUserController());1.2 核心内容Subject(形象主题角色):它申明了实在主题和代理主题的独特接口,使得在任何应用实在主题的中央都能够应用代理主题,客户端通常须要针对形象主题角色进行编程。Proxy(代理主题角色):代理主题角色外部蕴含了对实在主题的援用,从而能够在任何时候操作实在主题对象。在代理主题角色中提供一个与实在主题角色雷同的接口,以便在任何时候都能够代替实在主题。代理主题角色还能够管制对实在主题的应用,负责在须要的时候创立和删除实在主题对象,并对实在主题对象的应用加以束缚。通常,在代理主题角色中,客户端在调用所援用的实在主题操作之前或之后还须要执行其余操作,而不仅仅是单纯调用实在主题对象中的操作。RealSubject(实在主题角色):它定义了代理角色所代表的实在对象,在实在主题角色中实现了实在的业务操作,客户端能够通过代理主题角色间接调用实在主题角色中定义的操作。1.3 利用场景:业务零碎的非功能性需要开发 ...

July 12, 2023 · 3 min · jiezi

关于java:Shell-编程常用参考

Shell非凡变量 $0, $#, $*, $@, $?, $$和命令行参数Shell 中的非凡变量参考如下表: 变量含意$0以后脚本的文件名$n传递给脚本或函数的参数。n 是一个数字,示意第几个参数。例如,第一个参数是$1,第二个参数是$2$#传递给脚本或函数的参数个数$*传递给脚本或函数的所有参数$@传递给脚本或函数的所有参数。被双引号(" ")蕴含时,与 $* 稍有不同,上面将会讲到$?上个命令的退出状态,或上个函数的返回值$$以后Shell过程ID。对于 Shell 脚本,就是这些脚本所在的过程ID注:$* 和 $@ 的区别 $* 和 $@ 都示意传递给函数或脚本的所有参数,不被双引号(" ")蕴含时,都以"$1" "$2" … "$n" 的模式输入所有参数。然而当它们被双引号(" ")蕴含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的模式输入所有参数;"$@" 会将各个参数离开,以"$1" "$2" … "$n" 的模式输入所有参数。Shell 中的 if 判断条件测试的表达式if [ str1 = str2 ]:当两个串有雷同内容、长度时为真if [ str1 != str2 ]:当串str1和str2不等时为真if [ -n str1 ]:当串的长度大于0时为真(串非空)if [ -z str1 ]:当串的长度为0时为真(空串)if [ str1 ]:当串str1为非空时为真整数比拟-eq:测试两个整数是否相等-ne:测试两个整数是否不等-gt:测试一个数是否大于另一个数-lt:测试一个数是否小于另一个数-ge:大于或等于-le:小于或等于命令间的逻辑关系: 逻辑与:&&逻辑或:||字符串比拟字符串比拟,间接用 ==、!=、>、< 等符号即可,两边须要有空格。 文件测试-e:FILE 测试文件是否存在-f:file 测试文件是否为一般文件-d:file 测试指定门路是否为目录-r:file 测试文件对以后用户是否可读-w:file 测试文件对以后用户是否可写-x:file 测试文件对以后用户是都可执行我是梅小西,最近在某东南亚电商公司做 DevOps 的相干事件。从本期开始,将陆续分享基于 Jenkins 的 CI/CD 工作流,包含 Jenkins On k8s 等。 ...

July 12, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-Spring-AOP实战

这篇文章的目标是对上一篇文章无关AOP概念的复习和坚固,通过实战的形式。 引入依赖首先须要引入Spring Aspect依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version> </dependency>@EnableAspectJAutoProxy先申明一下,例子都应用注解形式实现,不必xml配置的形式了,其实区别不大,然而自己不喜爱xml配置的形式。 Configuration文件中退出@EnableAspectJAutoProxy注解。 @Configuration()@ComponentScan(value={"springAop"})@EnableAspectJAutoProxy@PropertySource("classpath:application.properties")@EnableAsyncpublic class MyConfiguration {}业务类筹备一个业务类UserService,指标就是为了实现AOP性能,所以业务类不须要实现任何业务逻辑: public interface IUserService { public boolean addUser(); public boolean deleteUser(); public boolean updateUser();}IUserService的实现类UserService,为了验证AfterThrowing类的Advice,咱们将updateUser办法间接抛出异样,模仿业务类抛异样的场景: package springAop;import org.springframework.stereotype.Component;@Componentpublic class UserService implements IUserService{ @Override public boolean addUser() { System.out.println("add user in userService"); return true; } @Override public boolean deleteUser() { System.out.println("delete user in userService"); return false; } @Override public boolean updateUser() { throw new RuntimeException("cant be updated..."); }}目前为止波及到的Spring AOP的概念:指标对象,也就是Target object,本例中指标对象就是UserService。 ...

July 11, 2023 · 3 min · jiezi

关于java:TPLINK面试真题和答案您能做对几道

话说 TP-LINK 联洲的秋招提前批曾经开启很久了,6 月份就曾经开启了,并且最近曾经有人陆陆续续拿到口头 Offer 了,所以明天就来给大家介绍一下 TP-LINK 的面试流程和真题及答案解析。 秋招提前批投递地址官网投递地址:https://hr.tp-link.com.cn/jobList TP-LINK 面试流程TP-LINK 整个面试流程如下: 技术面:两面或者是三面,一般 Offer 两面,SP Offer 三面。心理评测座谈会签约电话 OC签订三方协定 面试问题问题来源于某客,如下图所示: 问题链接:https://www.nowcoder.com/feed/main/detail/9af7b7989419489284b3cfce7aaae2bc答案解析1.说一下微服务架构?微服务是一种软件开发架构格调,用于构建简单应用程序。它将大型应用程序拆分成一系列较小、独立的服务,每个服务专一于实现特定的业务性能。这些服务之间通过轻量级的通信机制(通常是基于 HTTP 或 RPC)进行交互,能够独立部署、扩大和治理。 微服务的次要特点包含: 繁多责任:每个微服务专一于执行一个明确定义的业务性能。这使得开发人员能够更容易地了解和保护服务。松耦合:微服务之间是独立的,它们能够应用不同的编程语言、技术堆栈和数据存储。这种松耦合使得开发团队可能独立地开发、测试和部署各个服务。独立部署:每个微服务都能够独立地部署,这意味着当对一个服务进行更改时,不须要重新部署整个应用程序。这进步了开发和公布的速度,并容许疾速迭代和灵活性。弹性扩大:因为每个微服务是独立的,能够依据须要对它们进行独立的扩大。这使得应用程序可能更好地解决高负载状况,并具备更好的可伸缩性。无限上下文:每个微服务保护本人的数据存储,这意味着它们能够应用不同类型的数据库或存储技术。这种隔离有助于缩小整个零碎的复杂性,并进步可靠性。 2.微服务优缺点微服务架构具备以下长处: 松耦合:微服务架构使得各个服务之间的耦合度升高,每个服务都是独立的,能够应用不同的编程语言、技术堆栈和数据存储。这样能够进步团队的自治性,各个服务能够独立开发、测试和部署。可伸缩性:因为微服务是独立的,能够依据须要对每个服务进行独立的扩大。这意味着能够依据流量和负载的需要,对具体的服务进行程度扩大,进步零碎的性能和可用性。独立部署:每个微服务都能够独立地部署,这样在更新或修复某个服务时,不须要重新部署整个应用程序。这样能够升高危险,并进步开发和公布的速度。技术异构性:微服务架构容许不同的服务应用不同的技术和工具。这样能够抉择最适宜每个服务需要的技术,进步开发效率和灵活性。易于了解和保护:微服务架构将简单的应用程序拆分为小而独立的服务,每个服务专一于一个明确定义的业务性能。这样使得代码库更易于了解和保护。然而,微服务架构也存在一些挑战和毛病: 分布式系统复杂性:微服务架构中的服务是分布式的,须要解决服务间通信、数据一致性、错误处理等问题。这减少了零碎的复杂性,须要更多的设计和管理工作。服务间通信开销:因为微服务架构中的服务通过网络通信进行交互,会减少肯定的提早和开销。此外,须要实现适当的通信机制和协定来确保可靠性和数据一致性。运维复杂性:微服务架构中波及多个独立的服务,每个服务都须要独立进行监控、日志记录和故障排除。这减少了运维的复杂性,须要适当的工具和自动化来治理和监控服务。数据一致性:因为每个微服务保护本人的数据存储,确保数据一致性变得更加艰难。在跨多个服务的业务操作中,须要采取适当的策略和技术来保证数据的一致性和完整性。 3.负载平衡的实现算法负载平衡是指将网络流量或工作负载调配到多个服务器或计算资源上,以进步零碎的性能、可靠性和可扩展性。在实现负载平衡时,通常会采纳以下算法: 轮询(Round Robin):依照轮询的形式顺次将申请分发给后端服务器。每个申请依照程序顺次调配给不同的服务器,周而复始。这种算法简略且平衡,实用于服务器性能类似且无状态的状况。起码连贯(Least Connection):依据以后连接数抉择连接数起码的服务器来解决新的申请。这种算法能够无效地将负载平衡到连接数较少的服务器上,以放弃各服务器的负载绝对平衡。IP哈希(IP Hash):依据客户端的 IP 地址进行哈希计算,将同一个 IP 地址的申请发送到同一个后端服务器。这样能够确保同一个客户端的申请都发送到同一台服务器上,实用于须要放弃会话一致性的场景。加权轮询(Weighted Round Robin):给每个服务器调配一个权重值,依据权重值的比例来调配申请。具备较高权重的服务器会接管到更多的申请,实用于服务器性能不平衡的状况。加权起码连贯(Weighted Least Connection):依据服务器的以后连接数和权重值来抉择服务器。连接数越少且权重值越高的服务器会被优先选择。随机(Random):随机抉择一个后端服务器来解决申请。这种算法简略且平衡,但无奈保障每个服务器的负载统一。响应工夫加权(Response Time Weighted):依据服务器的均匀响应工夫或解决工夫来调配申请。响应工夫较短的服务器会失去更多的申请,以进步零碎整体的响应速度。这些算法能够独自应用,也能够联合应用,依据理论需要和场景进行抉择和配置。另外,古代的负载均衡器通常会联合实时监测和主动调整策略,依据服务器的负载状况动静地调整申请散发策略,以实现更智能和自适应的负载平衡。 4.Redis集群部署形式?Redis集群次要有以下三种模式: 主从复制(Master-Slave Replication):这是最简略的 Redis 集群部署形式。在主从复制中,一个节点作为主节点(master),负责解决写操作和读操作的局部负载;而其余节点作为从节点(slaves),复制主节点的数据,并负责读操作的负载。主节点负责写操作的原始数据,而从节点通过异步复制主节点的数据来提供读操作的负载平衡和高可用性。哨兵模式(Sentinel):Sentinel 模式用于提供 Redis 的高可用性。在这种部署形式中,有多个 Redis 实例,其中一个充当主节点,负责解决写操作和读操作的局部负载。同时,还有多个 Sentinel 节点,它们监控主节点的状态,并在主节点故障时主动将从节点晋升为新的主节点。这种形式能够实现故障切换和主动复原。Redis Cluster 模式:Redis Cluster 是 Redis 官网提供的分布式集群解决方案。它通过分区(sharding)将数据分布在多个节点上,每个节点负责一部分数据。Redis Cluster 应用哈希槽(hash slots)来治理数据分布,并在节点故障时进行主动迁徙和重新分配。客户端能够间接连贯到任何一个节点,节点会协调数据的读写操作。 5.MySQL主从复制?MySQL 主从复制是一种常见的数据复制技术,用于实现 MySQL 数据库的高可用性、读写拆散和数据备份等需要。在主从复制中,有一个主数据库(Master)和一个或多个从数据库(Slaves)。 MySQL 主从复制在确保了主服务器(Master)和从服务器(Slave)网络连接失常,能够相互拜访的状况下,通过配置(次要是主服务器开启 bin log),从服务同步 bin log 的形式就能够实现主从复制了。 ...

July 11, 2023 · 4 min · jiezi

关于java:javanetty多路复用

背景:传统IO模型的前提是网络传输数据,服务端接管并解决的细节过程, 传统IO解决的细节过程是,每一个tcp连贯应用一个线程解决,是否有读写事件的变量 和tcp连贯是一对一的关系,tcp连贯和线程也是一对一关系,毛病不言而喻,线程过多,服务的客户端数量被限度的很小netty的多路复用1:复用的是 "是否有读写事件的变量", 一个这样的变量对应多个tcp连贯,不同的值示意不同的状态netty的多路复用2:复用的是线程,来一个tcp连贯后,是把这个连贯的读写 调配给线程池里的某个线程,而不是创立新的线程.最终,线程池里的一个线程负责多个tcp的读写.

July 11, 2023 · 1 min · jiezi

关于java:Java程序性能分析内存

一、前言开发Java我的项目过程中,难免会碰到一些 性能 问题,这时候就须要一些工具,帮忙排查本文次要介绍 JDK自带的上古神器 jstat、jmap,用于剖析内存问题,另简略介绍 MAT、gceasy、HeapDump 等以 openjdk 11.0.13、G1 垃圾收集器、Linux零碎 为例二、GC剖析:jstat1. jstat 简介jstat 全称 “Java Virtual Machine statistics monitoring tool”,位于 JDK 的 bin 目录下,用于对 Java 程序的资源和性能进行监控,包含 Heap size、垃圾回收情况 等。jstat --help:查看命令帮忙jstat -options:返回有哪些命令选项,如 -gcutil、-gc、-gccapacity、-gccause,另有 -class、-compiler、-printcompilation 等jstat 上一步输入的命令选项 [-t] [-h每几行输入题目行] 过程号 [继续输入距离时长 [输入次数]]继续输入距离时长 默认毫秒,数字前面加 s 单位改为秒,-t 示意每行结尾输入 绝对利用启动工夫的Timestamp 工夫戳2. jstat -gcutil常用命令格局:jstat -gcutil 过程号 继续输入距离毫秒数,下图每隔 1000毫秒输入一次前6列 输入各个内存区域应用百分比 (没有容量大小),顺次是 幸存区survivor0、1、新生代Eden、老年代Old、元数据 Metaspace、Compressed class spaceGC 结尾的列 示意 GC次数,GCT 结尾的 示意 GC耗时,顺次是 Young GC 次数和耗时、Full GC、Compressed class space GC,最初一列 GCT 是 Total总GC耗时2次相邻的GC,能够疾速判断那一次GC的耗时;GCT / GC = 均匀每次GC耗时GC是否频繁规范参考:Young GC执行迅速(50毫秒以内)、Young GC执行不频繁(距离10秒左右一次)、Full GC执行迅速(1秒以内)、Full GC执行不频繁(距离10分钟左右一次) 3. jstat -gc列出 各区域的容量Capacity、应用大小 Utilization,单位是 KB,有容量大小,没有百分比YGC 开始,是各区域 GC次数、耗时 4. jstat -gccapacity次要关注 各区域 最小(Min,MN结尾)、最大(Max,MX结尾)、以后(Capacity,C结尾) 容量 capacity最初3列 YGC、FGC、CGC 别离是 Young、Full、Compressed class space 区域 GC次数NGCMN 是 新生代最小容量 new generation capacity min各个分区的容量,单位是 KB 三、内存剖析:jmap1. jmap 简介jmap 能够 疾速剖析简略的内存占用,生成 dump文件 便于后续剖析 ...

July 11, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-Spring-AOP-概念

什么是AOPAOP - Aspect Oriented Programming的缩写,面向切面编程。 面向切面编程AOP是面向对象程序设计OOP的一个补充或扩大,是为了解决OOP在一些共性问题上的有余应运而生的。比方记录日志、事务处理、性能剖析等等与业务无关的需要,须要产生在很多类的很多办法上,而需要是一样的:比方日志解决就是在办法执行前或执行后记录日志,事务处理须要在办法执行前开启事务、在办法执行完结后提交或回滚事务。 AOP呈现之前,老程序员应该都有记忆(亲身经历),咱们须要在每一个须要事务处理的办法中都增加逻辑截然不同的代码:获取连贯、开启事务。而后在办法执行实现后也须要增加截然不同的代码:如果办法执行胜利则提交事务、执行失败或产生异样则回滚事务。 这些代码写多了(其实不可能少、必定会很多),能让程序员痛苦不堪、狐疑人生! 而后,AOP横空出世。 AOP其实最终要实现的性能就是:不侵入代码的前提下,实现上述这些公共逻辑。 我艹,几乎不要太爽...... 当然,横空出世的不是Spring AOP,那个时候还没有Spring。AOP横空出世之后,为了对立AOP的概念、属于以及接口,呈现了AOP联盟,Spring AOP也遵循AOP联盟的相干标准。之后,AOP江湖逐步被AspectJ统治,Spring从2.0开始就无缝集成了AspectJ。 AOP的底层逻辑其实不简单,就是通过代理:须要实现这些公共逻辑的类,咱们能够称之为指标类,不批改指标类、却要扭转(或者叫加强)其执行逻辑,用脚指头都能想到是通过代理。 AOP的晚期版本(AspectJ 1.0)通过动态代理实现,动态代理是在编译期批改指标类代码、生成代理类代码的形式,运行会比拟快(因为在编译器就生成代理对象代码了),然而显著的毛病就是稍有批改就须要从新编译打包,不堪称不繁琐。 之后的AOP版本通过动静代理实现,动静代理咱们也很相熟,后面专门剖析过,是在运行期动静生成指标类的代理对象的,无非两种形式: JDK动静代理,基于接口实现。Cglib动静代理,生成指标类的扩大类实现。具体请参考动静代理 java原生 vs Cglib所以咱们当初应该明确,AOP是什么,为什么会有AOP,以及AOP的底层原理。 AOP术语我始终在想,对于AOP的初学者,是通过间接上手练习的形式入门,还是依照惯例的形式:先学习AOP的实践,把握AOP的概念和术语,而后再通过简略示例增强了解。 惯例学习路线应该是后者,然而,尽管AOP的概念和底层逻辑很好了解,然而AOP的术语却极具迷惑性、十分不敌对,很难了解。 然而的确没有方法,要学习AOP,那么AOP的术语就是迈不过来的一道坎,无论如何也是要有所理解的。 所以,来吧,看看AOP相干术语,咱们尽量用简单明了的语言来解释。 Target object: 指标对象,指的就是咱们的业务对象,咱们要通过AOP技术加强指标对象的办法(对于Spring Aop来说,只加强指标对象的办法,对于AOP联盟来说,还能够加强属性)。AOP proxy:AOP代理对象,后面曾经说过AOP的底层逻辑就是通过代理实现的,AOP proxy指的就是指标对象的代理对象。Join point:连接点,其实是AOP要加强的点,对于Spring AOP来说,咱们要加强的其实就是指标对象的办法执行过程,所以连接点就是办法执行。Pointcut:切点,指的是满足条件的连接点,或者能够了解为满足切入连接点的条件。切点通过表达式的形式定义条件,满足切点条件的时候,执行切入动作、加强连接点规定的办法。Advice:加强逻辑,是AOP要实现的目标的体现,比方实现事务管理的AOP,Advice就是开启及提交或回滚事务,日志解决的AOP,Advice就是在连接点办法执行前后打印日志。Aspect: 切面,简略说就是体现连接点Join point定义、Pointcut定义、以及Advice逻辑实现的模块(能够简略了解为:一组类)。Spring Aop中通过xml配置文件指定、或者通过注解@Aspect指定。Introduction:推荐(其实不翻译最好,AOP的这些术语其实都是这样,不翻译、直呼其英文名最好),能够这么了解:AOP切面类中,除为了实现目标类办法加强之外,还能够定义其余的属性或办法,去帮忙实现AOP之外的额定的性能。Weaving:织入,就是依据Aspect的定义,对符合条件的指标对象,通过动静代理技术加强其办法逻辑、生成代理对象的过程。生成代理对象之后,就相当于咱们把Advice“织入”到代理对象中了。Spring AOP的Advice类型: Before advice:在join point执行前失效,然而advice没有拦挡阻止join point的能力,起因是Spring Aop的Before advice对指标对象的join point的调用控制权并不在切面类中,而是交给了框架,所以除非有异样产生、否则无奈拦挡或阻止join point办法的执行。After returning advice: join point失常执行实现并返回后失效。After throwing advice: join point调用异样后失效。After (finally) advice: join point调用后失效,不论失常完结或抛出异样。Around advice: Spring AOP性能最为弱小的advice,功能强大的起因是:对join point的调用是在切面实现的,所以,切面编写者有权对join point调用前、调用后的逻辑做全面的管制,比方能够在调用前进行逻辑判断后阻止join point的调用,返回后面自定义的返回值或者抛出异样,也能够在join point调用后依据其返回值做任意的解决。既然Around advice能够对join point做调用前、调用后的加强,而且其性能最为弱小,那是不是就意味着before和after加强就没有存在的意义了呢?无关这一点,Spring官网其实也给出了一些倡议: Around advice is the most general kind of advice. Since Spring AOP, like AspectJ, provides a full range of advice types, we recommend that you use the least powerful advice type that can implement the required behavior. For example, if you need only to update a cache with the return value of a method, you are better off implementing an after returning advice than an around advice, although an around advice can accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors. For example, you do not need to invoke the proceed() method on the JoinPoint used for around advice, and, hence, you cannot fail to invoke it.一句话:抉择刚好能满足本人需要的Advice、而不倡议抉择性能远超出本人需要的Advice,一方面简化切面逻辑,另一方面,缩小调用谬误的产生概率。 ...

July 10, 2023 · 3 min · jiezi

关于java:Java-8中读取文件内容-Fileslines-方法如何使用

Files.lines() 办法是 Java 8 中在 java.nio.file.Files 类中提供的一个用于逐行读取文本文件的办法。它返回一个流(Stream<String>),其中每个元素都是文件的一行内容。Files.lines() 办法有两个重载的办法:1. Stream<String> lines(Path path, Charset charset): path:要读取的文件门路,能够应用 Paths.get(String) 办法创立一个 Path 对象。charset:可选参数,指定文件的字符编码。如果不提供字符编码,则默认应用平台的默认字符编码。2. Stream<String> lines(Path path): path:要读取的文件门路,能够应用 Paths.get(String) 办法创立一个 Path 对象。此办法应用平台的默认字符编码对文件进行解码。以下是应用 Files.lines() 办法的示例: import java.io.IOException;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.util.stream.Stream;public class FileLinesExample { public static void main(String[] args) { String filePath = "path/to/file.txt"; // 应用指定的字符编码读取文件的每一行 try (Stream<String> lines = Files.lines(Paths.get(filePath), StandardCharsets.UTF_8)) { lines.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); } // 应用默认字符编码读取文件的每一行 try (Stream<String> lines = Files.lines(Paths.get(filePath))) { lines.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); } }}以上示例展现了如何应用 Files.lines() 办法逐行读取文件的内容,别离应用指定的字符编码和默认的字符编码。在解决完流后,咱们应用了 try-with-resources 来确保流被正确敞开,并捕捉了可能的 IOException。 ...

July 10, 2023 · 1 min · jiezi

关于java:用-Java-代码读取本地两个TXT文件并且以行为单位比较两个文件的不同

以下是应用Java代码读取本地两个TXT文件,并以行为单位比拟两个文件的不同的示例:import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;import java.util.List;public class FileComparator { public static void main(String[] args) { String fileAPath = "C:/Users/MAO/Desktop/A.txt"; String fileBPath = "C:/Users/MAO/Desktop/B.txt"; List<String> fileALines = readFileLines(fileAPath); List<String> fileBLines = readFileLines(fileBPath); List<String> uniqueLinesInFileA = new ArrayList<>(); for (String line : fileALines) { if (!fileBLines.contains(line)) { uniqueLinesInFileA.add(line); } } List<String> uniqueLinesInFileB = new ArrayList<>(); for (String line : fileBLines) { if (!fileALines.contains(line)) { uniqueLinesInFileB.add(line); } } // 文件A中不在文件B中的行 System.out.println("Lines in file A that are not in file B:"); for (String line : uniqueLinesInFileA) { System.out.println(line); } // 文件B中不在文件A中的行 System.out.println("Lines in file B that are not in file A:"); for (String line : uniqueLinesInFileB) { System.out.println(line); } } private static List<String> readFileLines(String filePath) { List<String> lines = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { String line; while ((line = reader.readLine()) != null) { lines.add(line); } } catch (IOException e) { e.printStackTrace(); } return lines; }}用JAVA 8格调能够这样写: ...

July 10, 2023 · 2 min · jiezi

关于java:再见Guava再见Ehcache

一、Caffeine介绍1、缓存介绍缓存(Cache)在代码世界中无处不在。从底层的CPU多级缓存,到客户端的页面缓存,处处都存在着缓存的身影。缓存从实质上来说,是一种空间换工夫的伎俩,通过对数据进行肯定的空间安顿,使得下次进行数据拜访时起到减速的成果。 就Java而言,其罕用的缓存解决方案有很多,例如数据库缓存框架EhCache,分布式缓存Memcached等,这些缓存计划实际上都是为了晋升吞吐效率,防止长久层压力过大。 对于常见缓存类型而言,能够分为本地缓存以及分布式缓存两种,Caffeine就是一种优良的本地缓存,而Redis能够用来做分布式缓存 2、Caffeine介绍Caffeine官网: https://github.com/ben-manes/caffeineCaffeine是基于Java 1.8的高性能本地缓存库,由Guava改良而来,而且在Spring5开始的默认缓存实现就将Caffeine代替原来的Google Guava,官网阐明指出,其缓存命中率曾经靠近最优值。实际上Caffeine这样的本地缓存和ConcurrentMap很像,即反对并发,并且反对O(1)工夫复杂度的数据存取。二者的次要区别在于: ConcurrentMap将存储所有存入的数据,直到你显式将其移除;Caffeine将通过给定的配置,主动移除“不罕用”的数据,以放弃内存的正当占用。因而,一种更好的了解形式是:Cache是一种带有存储和移除策略的Map。 二、Caffeine根底应用Caffeine,须要在工程中引入如下依赖 <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <!--https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeinez找最新版--> <version>3.0.5</version></dependency>举荐一个开源收费的 Spring Boot 实战我的项目: https://github.com/javastacks/spring-boot-best-practice1、缓存加载策略1.1 Cache手动创立最一般的一种缓存,无需指定加载形式,须要手动调用put()进行加载。须要留神的是put()办法对于已存在的key将进行笼罩,这点和Map的体现是统一的。在获取缓存值时,如果想要在缓存值不存在时,原子地将值写入缓存,则能够调用get(key, k -> value)办法,该办法将防止写入竞争。调用invalidate()办法,将手动移除缓存。 在多线程状况下,当应用get(key, k -> value)时,如果有另一个线程同时调用本办法进行竞争,则后一线程会被阻塞,直到前一线程更新缓存实现;而若另一线程调用getIfPresent()办法,则会立刻返回null,不会被阻塞。 Cache<Object, Object> cache = Caffeine.newBuilder() //初始数量 .initialCapacity(10) //最大条数 .maximumSize(10) //expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准 //最初一次写操作后通过指定工夫过期 .expireAfterWrite(1, TimeUnit.SECONDS) //最初一次读或写操作后通过指定工夫过期 .expireAfterAccess(1, TimeUnit.SECONDS) //监听缓存被移除 .removalListener((key, val, removalCause) -> { }) //记录命中 .recordStats() .build(); cache.put("1","张三"); //张三 System.out.println(cache.getIfPresent("1")); //存储的是默认值 System.out.println(cache.get("2",o -> "默认值"));1.2 Loading Cache主动创立LoadingCache是一种主动加载的缓存。其和一般缓存不同的中央在于,当缓存不存在/缓存已过期时,若调用get()办法,则会主动调用CacheLoader.load()办法加载最新值。调用getAll()办法将遍历所有的key调用get(),除非实现了CacheLoader.loadAll()办法。应用LoadingCache时,须要指定CacheLoader,并实现其中的load()办法供缓存缺失时主动加载。 在多线程状况下,当两个线程同时调用get(),则后一线程将被阻塞,直至前一线程更新缓存实现。 LoadingCache<String, String> loadingCache = Caffeine.newBuilder() //创立缓存或者最近一次更新缓存后通过指定工夫距离,刷新缓存;refreshAfterWrite仅反对LoadingCache .refreshAfterWrite(10, TimeUnit.SECONDS) .expireAfterWrite(10, TimeUnit.SECONDS) .expireAfterAccess(10, TimeUnit.SECONDS) .maximumSize(10) //依据key查询数据库外面的值,这里是个lamba表达式 .build(key -> new Date().toString());1.3 Async Cache异步获取AsyncCache是Cache的一个变体,其响应后果均为CompletableFuture,通过这种形式,AsyncCache对异步编程模式进行了适配。默认状况下,缓存计算应用ForkJoinPool.commonPool()作为线程池,如果想要指定线程池,则能够笼罩并实现Caffeine.executor(Executor)办法。synchronous()提供了阻塞直到异步缓存生成结束的能力,它将以Cache进行返回。 ...

July 10, 2023 · 3 min · jiezi

关于java:华为云出品深入理解高并发编程Java线程池核心技术电子书发布

零碎拆解线程池外围源码的开源小册 透过源码看清线程池背地的设计和思路 具体解析AQS并发工具类* 点击下方链接进入官网,右上角搜寻框搜寻“《深刻了解高并发编程:Java线程池核心技术》” 即可获取下载。 https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&service=https%3A%2F%2Fwww.huaweicloud.com%2Fproduct%2Fcloudpipeline.html%3Futm_medium%3Dhdc#/login 精彩导读很多人感觉读源码比拟干燥,的确,读源码是要比看那些外表教你如何应用的文章要干燥的多,也比不上刷抖音和微博来的轻松愉快。 然而,读源码是一名程序员冲破自我瓶颈,取得高薪和升职加薪的一个有效途径。通过浏览优良的开源框架的源码,咱们可能领略到框架作者设计框架的思维和思路,从中学习优良的架构设计和代码设计。 当你只停留在业务层面的CRUD开发而不思进取时,工作几年之后,你会发现你简直除了应用啥都不会!此时,你退职场其实是毫无竞争劣势的。你所反反复复做的工作对于刚入行的毕业生来说,给他们3个月工夫,他们就能纯熟上手。 而你,反反复复做了几年的CRUD,没啥扭转。对于企业来说,他们更加违心雇佣那些老本低廉的老手,而不愿雇佣你!为啥?因为你给企业产出的价值未必比新入行的老手高,而你为企业带来的老本却远远高于老手! 看到这里,晓得为啥你工作几年后,想跳槽时,面试一个月薪几万+的职位,却只能俯视叹气了吧!!而比你工作年限少的人,却可能轻松面试比你薪资高出好几倍的职位!!不是他们运气好,而是他们比你把握了更加深刻的技能!! 当你在几年的工作工夫里做的都是CRUD时,其实你的工作教训只有3个月;当你在3个月里,充沛为本人布局好,在把握根底业务开发的同时,抽时间为本人充电,把握一些更加深刻的技能,则你的工作教训会高于那些混迹职场几年的CRUD人员。

July 10, 2023 · 1 min · jiezi

关于java:后端面经JavaIO多路复用-简录

0. Java 线程IO模型Java当中的线程I/O模型如图所示: 1. BIO当一个线程进行I/O操作的时候,传统的做法是阻塞期待,直到I/O操作实现再持续后续的操作,这种IO形式就是BIO(Blocking I/O)。 BIO形式的毛病是: 大量并发线程的场景下效率过低;空期待浪费资源;2. NIOJDK1.4引入了NIO(No Blocking I/O或者是New I/O)。NIO是一种同步非阻塞的I/O模型,绝对于BIO,NIO容许一个线程在I/O操作的时候解决其余工作,然而须要定期轮询查看I/O操作是否实现。NIO的毛病在于: 轮询的工夫距离不好把握;一个线程解决一个I/O操作,如果存在大量I/O,解决其余工作和轮询操作重复切换状态,上下文切换开销大;3. I/O多路复用(次要)3.1 概念为了解决NIO的毛病,Linux引入了I/O多路复用的机制,即一个线程能够同时监听多个I/O操作,当某个I/O操作实现后,会告诉线程进行解决。多路指的是多个SOCKET连贯之间的I/O操作,复用指的是共用一个线程。I/O多路复用的长处在于: 一个线程能够同时监听多个I/O操作,缩小了线程的数量,防止了线程切换的开销;须要留神的是,I/O多路复用只有和NIO配合应用能力发挥作用,因为NIO是非阻塞的,所以能够在一个线程中同时监听多个I/O操作,而BIO是阻塞的,一个线程只能解决一个I/O操作,所以无奈实现I/O多路复用。 3.2 实现I/O多路复用的实现思路:对于多个socket连贯,程序提供一个文件描述符汇合给零碎,当某个接口的I/O操作实现后,会告诉线程进行解决。 实现I/O多路复用的形式有三种:select、poll、epoll。 1. select函数原型如下所示: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);nfds:文件描述符的数量,即文件描述符汇合中最大的文件描述符加1;readfds:读文件描述符汇合;writefds:写文件描述符汇合;exceptfds:异样文件描述符汇合;timeout:超时工夫;从参数能够看进去select形式监听读、写、异样事件。 select依据监听的事件类型别离创立三个文件描述符数组,而后在timeout工夫内阻塞线程进行监听,直到有事件产生或者超时。之后查看数组中是否有事件达到。select的毛病在于: 文件描述符数组大小无限,为1024,因而对于高并发场景并不实用;维持三个文件描述符数组,占据大量的内存空间;每次调用select须要将数组从用户空间拷贝到内核空间,同时从新对数组进行遍历查找,效率低;2. poll函数原型如下所示: int poll(struct pollfd *fds, nfds_t nfds, int timeout);fds:文件描述符数组;ndfs:文件描述符数组的大小;timeout:超时工夫;实质的工作过程和select相似,然而略微做了改良,只须要构建一个数组,并且数组大小不受限制,而是可能自在指定;poll的毛病在于: 每次调用poll之后都须要进行数组遍历,这一点并没有改良3. epoll为了解决select和poll的毛病,在高并发场景下,不同的操作系统引入了不同的解决方案,例如Linux引入了epoll、FreeBSD引入了kqueue、Solaris引入了/dev/poll。由epoll实现I/O多路复用,步骤如下: 先创立epoll对象: int epfd = epoll_create(10);其中,int epoll_create(int size)会在内核空间开拓一块指定大小的数据表,并由epfd指向这部分内存。 创立好epoll对象之后,应用epoll_ctl将注册须要监听的事件: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epfd是创立数组之后的内存指针;op是操作类型,包含三种模式: EPOLL_CTL_ADD:增加须要监听的事件;EPOLL_CTL_MOD:批改须要监听的事件;EPOLL_CTL_DEL:删除须要监听的事件;fd是须要监听的文件描述符,须要反对NIO;event记录了注册事件的具体信息。数据结构如下所示: typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64;} epoll_data_t;struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */};应用epoll_wait进行监听:epoll_wait函数原型如下所示: ...

July 10, 2023 · 1 min · jiezi

关于java:使用-SaToken-实现不同的登录模式单地登录多地登录同端互斥登录

一、需要剖析如果你常常应用腾讯QQ,就会发现它的登录有如下特点:它能够手机电脑同时在线,然而不能在两个手机上同时登录一个账号。 同端互斥登录,指的就是:像腾讯QQ一样,在同一类型设施上只容许单地点登录,在不同类型设施上容许同时在线。 动静演示图: Sa-Token 是一个轻量级 java 权限认证框架,次要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相干问题。Gitee 开源地址:https://gitee.com/dromara/sa-token本文将介绍在 Sa-Token 中,如何实现以下登录策略: 单地登录:指一个账号同一时间只能在一个中央登录,新登录会挤掉旧登录,也能够叫:单端登录。多地登录:指一个账号同一时间能够在不同中央登录,新登录会和旧登录共存,也能够叫:多端登录。同端互斥登录:在同一类型设施上只容许单地点登录,在不同类型设施上容许同时在线,参考腾讯QQ的登录模式:手机和电脑能够同时在线,但不能两个手机同时在线。与之对应的,登记策略也将分为以下几种: 单端登记:只在调用退出的一端登记。全端登记:一端登记,全端下线。同端登记:例如将所有手机端登记下线,PC端不受影响。二、多地登录此模式较为简单,Sa-Token 默认模式即为多地登录模式。 1、首先引入 Sa-Token 依赖:<!-- Sa-Token 权限认证 --><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version></dependency>注:如果你应用的是 SpringBoot 3.x,只须要将 sa-token-spring-boot-starter 批改为 sa-token-spring-boot3-starter 即可。 2、在用户登录时将账号id写入会话中@RestController@RequestMapping("/user/")public class UserController { @RequestMapping("doLogin") public SaResult doLogin(String username, String password) { // 此处仅作示例模仿,实在我的项目须要从数据库中查问数据进行比对 if("zhang".equals(username) && "123456".equals(password)) { StpUtil.login(10001); return SaResult.ok("登录胜利"); } return SaResult.ok("登录失败"); }}启动类: @SpringBootApplicationpublic class SaTokenDemoApplication { public static void main(String[] args) { SpringApplication.run(SaTokenDemoApplication.class, args); System.out.println("\n启动胜利:Sa-Roken 配置如下:" + SaManager.getConfig()); }}如上代码,在多人登录同一账号时将不会对旧会话做任何解决,同一账号能够在多个地点任意登录,互不影响。 ...

July 10, 2023 · 1 min · jiezi

关于java:暗黑2能用Java开发还能生成APP

最近烧哥发现个宝藏我的项目,居然用Java开发了暗黑2进去。家喻户晓,暗黑2是暴雪开发的一款经典游戏,距今虽有20多年,依然有很多粉丝。 粉丝连续激情的形式有很多,一种是做Mod,比方魔电,对怪物、技能、物品、场景、甚至游戏机制都有大改,目前还在定期更新,并有战网提供。 另一种是纯念旧,用另外的编程语言从新制作,没想到居然有Java出场。 暴雪原版是C++开发,商业游戏,没有源码,但国外大佬早已破译90%以上的逻辑,为各种重制提供了可能。 首先是OpenDiablo2,最开始用golang,前面因为性能问题改为C++、Lua。 而后是devilution,基于Unity、C#。 接下来就到了riiablo,基于Java、LibGDX。 还是看下界面: 是不是原汁原味,还带点现代感?外面的资源都是从原版MPQ里提取,运行时也须要用户手动提供。 而且还提供了安卓版,第一次启动挺慢,要解压很多资源: 模拟器上运行的,可能有点失真,白球能够触摸管制走动,上面一排别离为角色、工作、物品、技能等。 据调查,Java的游戏引擎次要有以下几种: 基于Box2D和LiquidFun的JBox2D基于pybullet的Bullet基于bump.lua的jbump基于JavaFX的FXGLLWJGLJMonkeyEnginelibGDX最初,Act4,谁能通知我这是什么?

July 9, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-ApplicationContext

先看一下ApplicationContext的类构造: 可知:ApplicationContext是BeanFactory,所以具备BeanFactory的能力:初始化Spring容器(细节下一篇文章剖析),除此之外,ApplicaitonContext还具备如下能力: MessageSource接口提供的i18n能力。ResourceLoader接口提供的资源文件拜访能力。ApplicationListener接口提供的事件公布及监听能力。HierarchicalBeanFactory提供的能力。明天次要关注前3项。 i18n能力即便不是国际化我的项目,i18n也很实用,比方我的项目中的提示信息或错误信息的治理,切实是见过太多我的项目对错误信息的放任不管,其实提示信息或者错误信息是零碎与用户交互的重要组成部分,有必要认真对待:i18n就是一个抉择。 ApplicationContext通过MessageSource接口提供了i18n能力,简略易用。 Spring提供了MessageSource的3个实现:ResourceBundleMessageSource, ReloadableResourceBundleMessageSource和 StaticMessageSource。咱们就以ResourceBundleMessageSource举例。 咱们晓得应用i18n须要以下步骤: 创立property文件,定义message创立MessageSource加载propperty文件通过MessageSource获取property文件定义的message上面咱们以xml和注解两种形式举例。 1. 创立property文件创立property文件是通用的,两种形式无区别,所以咱们首先创立property文件。 为了反对中文,咱们须要把我的项目的文件编码方式设置为UTF-8、property文件的编码方式抉择为UTF-8,并且肯定勾选native-to-accii这一选项,否则会呈现中文乱码: 而后在resources目录下创立一个myMessage的、蕴含en和zh两个语言resource Bundle文件,创立实现后如下图: 关上myMessage_en.propperties文件,输出: msg=this is {0}关上myMessage_zh.properties文件输出: msg=我其实就是要汉语的提示信息 {0}这个时候到classpath下看一下myMessage_zh.properties: msg=\u7EC8\u4E8E\u53EF\u4EE5\u6C49\u5B57\u4E86\u554A {0}能够看到是转换为accii码当前的文件了,这个就是native-to-ascii选项的作用,如果这里看到的仍然是汉字,最终进去的大概率是乱码。 好了,properties文件创建好了。 xml形式实现i18n还是用咱们以前的例子,在mySpring.xml文件中退出如下配置信息,把ResourceBundleMessageSource类加载到Spring容器中并指定配置文件名称为myMessage: <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>myMessage</value> </list> </property> </bean>而后在xml启动类中退出对message的调用代码: public class AppByXML { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mySpring.xml"); String msg = applicationContext.getMessage("msg",new Object[]{"汉字"},"default msg", Locale.CHINESE); System.out.println("msg:"+msg); }}执行启动类: msg:我其实就是要汉语的提示信息 汉字而后批改一下启动类,让他调用en配置文件: public class AppByXML { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mySpring.xml"); String msg = applicationContext.getMessage("msg",new Object[]{"english"},"default msg", Locale.ENGLISH); System.out.println("msg:"+msg); }执行启动类: ...

July 9, 2023 · 1 min · jiezi

关于java:Spring-Cloud-Alibaba-之-Nacos精讲

简介:java系列技术分享(继续更新中...) 初衷:一起学习、一起提高、坚定不移 如果文章内容有误与您的想法不统一,欢送大家在评论区斧正 心愿这篇文章对你有所帮忙,欢送点赞 珍藏 ⭐留言 一、Nacos简介官网 : http://nacos.io/ 官网文档:https://nacos.io/zh-cn/docs/what-is-nacos.html Nacos是Alibaba微服务生态组件中的重要组件之一,次要用它实现利用的动静服务发现、配置管理、服务治理。 Nacos 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生利用的动静服务发现、配置管理和服务治理平台。 Nacos 致力于帮忙您发现、配置和治理微服务。Nacos 提供了一组简略易用的个性集,帮忙您疾速实现动静服务发现、服务配置、服务元数据及流量治理。 Nacos 帮忙您更麻利和容易地构建、交付和治理微服务平台。 Nacos 是构建以“服务”为核心的古代利用架构 (例如微服务范式、云原生范式) 的服务基础设施。 二、版本抉择应用spring cloud alibaba时特地须要留神版本间的兼容关系请查看github官网wiki首页的: 版本阐明文档 三、下载安装 nacos下载 nacos-server :地址 https://github.com/alibaba/nacos/releases Windows间接下载zip包Linux下载tar.gz包docker在线装置查看文档: Docker如何装置NacosWindows目录如下: bin : 存储的就是可执行文件conf:存储的是nacos的配置文件 启动 进入nacos/bin目录中,执行如下命令启动 .\startup.cmd -m standalone #以单机模式启动输入nacos is starting with standalone 即为胜利。 进入可视化页面http://localhost:8848/nacos/ ,账号密码都是nacos,进行登录即可搭建胜利,Linux 装置逻辑统一 四、注册核心Nacos Discovery文档 本人我的项目依据 版本抉择进行确定 首先申明:该文章的版本为: Spring Boot ------------>2.6.3Spring Cloud ------------>2021.0.1Spring Cloud-Alibaba--------->2021.0.1.04.1 服务注册4.1.1 父工程引入Spirng-Cloud-Alibaba的治理依赖` <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.3</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version> </properties> <dependencyManagement> <!-- springCloud --> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- spring cloud alibaba是阿里巴巴团体针对服务开发所提供的一套解决方案 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.2</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>4.1.2 增加Nacos的客户端依赖 <!-- nacos作为注册核心的依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>application.yml:增加配置 ...

July 9, 2023 · 2 min · jiezi

关于java:聊聊如何自定义parallelStream的线程池

序本文次要钻研一下parallelStream怎么应用自定义的线程池 ForkJoinPooljava/util/concurrent/ForkJoinPool.java public class ForkJoinPool extends AbstractExecutorService { public ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, boolean asyncMode) { this(checkParallelism(parallelism), checkFactory(factory), handler, asyncMode ? FIFO_QUEUE : LIFO_QUEUE, "ForkJoinPool-" + nextPoolId() + "-worker-"); checkPermission(); } private ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, int mode, String workerNamePrefix) { this.workerNamePrefix = workerNamePrefix; this.factory = factory; this.ueh = handler; this.config = (parallelism & SMASK) | mode; long np = (long)(-parallelism); // offset ctl counts this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK); } private static ForkJoinPool makeCommonPool() { int parallelism = -1; ForkJoinWorkerThreadFactory factory = null; UncaughtExceptionHandler handler = null; try { // ignore exceptions in accessing/parsing properties String pp = System.getProperty ("java.util.concurrent.ForkJoinPool.common.parallelism"); String fp = System.getProperty ("java.util.concurrent.ForkJoinPool.common.threadFactory"); String hp = System.getProperty ("java.util.concurrent.ForkJoinPool.common.exceptionHandler"); if (pp != null) parallelism = Integer.parseInt(pp); if (fp != null) factory = ((ForkJoinWorkerThreadFactory)ClassLoader. getSystemClassLoader().loadClass(fp).newInstance()); if (hp != null) handler = ((UncaughtExceptionHandler)ClassLoader. getSystemClassLoader().loadClass(hp).newInstance()); } catch (Exception ignore) { } if (factory == null) { if (System.getSecurityManager() == null) factory = new DefaultCommonPoolForkJoinWorkerThreadFactory(); else // use security-managed default factory = new InnocuousForkJoinWorkerThreadFactory(); } if (parallelism < 0 && // default 1 less than #cores (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0) parallelism = 1; if (parallelism > MAX_CAP) parallelism = MAX_CAP; return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE, "ForkJoinPool.commonPool-worker-"); }}parallelStream默认应用的是common的ForkJoinPool,能够通过零碎属性来设置parallelism等ForkJoinPoolFactoryBeanorg/springframework/scheduling/concurrent/ForkJoinPoolFactoryBean.java ...

July 8, 2023 · 2 min · jiezi

关于java:Java-8函数式接口之FunctionT-R和BiFunctionT-U-R的区别

Function<T, R> 和 BiFunction<T, U, R> 是 Java 8 中的函数式接口,用于示意函数(Function),行将一个或多个输出映射为输入的操作。它们之间的区别在于承受的参数个数。1. Function<T, R>:承受一个参数并返回一个后果的函数。它定义了一个名为 apply(T t) 的形象办法,用于承受一个参数并将其映射为一个后果。常见的用法是对单个输出进行转换或解决。例如: Function<Integer, String> intToString = num -> "Number: " + num;System.out.println(intToString.apply(42)); // 输入: Number: 422. BiFunction<T, U, R>:承受两个参数并返回一个后果的函数。它定义了一个名为 apply(T t, U u) 的形象办法,用于承受两个参数并将它们映射为一个后果。通常用于须要对两个输出进行转换或解决的场景。例如: BiFunction<Integer, Integer, Integer> sum = (num1, num2) -> num1 + num2;System.out.println(sum.apply(2, 3)); // 输入: 5总结来说,Function<T, R> 是一个承受单个参数并返回后果的函数,而 BiFunction<T, U, R> 是一个承受两个参数并返回后果的函数。它们都被宽泛用于函数式编程和汇合操作中,用于对输出进行转换、映射或计算。

July 7, 2023 · 1 min · jiezi

关于java:Java-8函数式接口之ConsumerT和BiConsumerT-U的区别

Consumer<T> 和 BiConsumer<T, U> 是 Java 8 中的函数式接口,用于示意消费者操作(Consumer),即承受一些输出并对其执行特定操作的函数接口。它们之间的区别在于承受的参数个数。1. Consumer<T>:承受一个参数并执行操作的消费者。它定义了一个名为 accept(T t) 的形象办法,用于承受一个参数并对其进行操作。通常用于须要对单个输出进行解决的场景。例如: Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());printUpperCase.accept("hello"); // 输入: HELLO2. BiConsumer<T, U>:承受两个参数并执行操作的消费者。它定义了一个名为 accept(T t, U u) 的形象办法,用于承受两个参数并对它们进行操作。通常用于须要对两个输出进行解决的场景。例如: BiConsumer<String, Integer> printLength = (str, num) -> System.out.println(str + " 的长度是 " + num);printLength.accept("hello", 5); // 输入: hello 的长度是 5总结来说,Consumer<T> 是一个接管单个参数并执行操作的消费者,而 BiConsumer<T, U> 是一个接管两个参数并执行操作的消费者。它们都被宽泛用于函数式编程和汇合操作中,能够对元素或对象进行各种操作,如打印、转换、批改等。

July 7, 2023 · 1 min · jiezi

关于java:Java-8函数式接口之PredicateT-和-BiPredicateT-U的区别

Predicate<T> 和 BiPredicate<T, U> 都是 Java 8 中的函数式接口,用于示意谓词(Predicate),即能够对给定参数进行条件判断的操作。它们之间的区别在于承受的参数个数。1. Predicate<T>:承受一个参数并返回一个布尔值的谓词。它定义了一个名为 test(T t) 的形象办法,用于对给定的参数进行条件判断。常见的用法是过滤汇合中的元素,依据条件判断是否保留或排除某些元素。例如: Predicate<Integer> isEven = num -> num % 2 == 0;System.out.println(isEven.test(4)); // 输入: trueSystem.out.println(isEven.test(5)); // 输入: false2. BiPredicate<T, U>:承受两个参数并返回一个布尔值的谓词。它定义了一个名为 test(T t, U u) 的形象办法,用于对给定的两个参数进行条件判断。通常用于须要两个输出参数的判断场景。例如: BiPredicate<Integer, String> isPair = (num, str) -> num % 2 == 0 && str.length() > 5;System.out.println(isPair.test(4, "Hello")); // 输入: trueSystem.out.println(isPair.test(3, "Hello")); // 输入: falseSystem.out.println(isPair.test(4, "Hi there")); // 输入: false总结来说,Predicate<T> 是一个接管单个参数的谓词,而 BiPredicate<T, U> 是一个接管两个参数的谓词。它们都被宽泛用于函数式编程和汇合操作中,能够依据给定的条件对元素进行过滤、筛选和判断。

July 7, 2023 · 1 min · jiezi

关于java:Java-8函数式接口之Consumer用法

Consumer<T> 是 Java 8 中的一个预约义函数式接口,用于示意承受一个输出参数 T 并执行某些操作但没有返回值的操作。 Consumer<T> 接口中定义了一个形象办法 void accept(T t),该办法承受一个参数 t,示意要执行的操作。你能够应用 accept() 办法来定义具体的操作逻辑。 以下是应用 Consumer<T> 接口的示例代码: import java.util.Arrays;import java.util.List;import java.util.function.Consumer;public class ConsumerExample { public static void main(String[] args) { // 定义一个字符串列表 List<String> fruits = Arrays.asList("Apple", "Orange", "Banana", "Mango"); // 应用 Consumer 接口实现遍历输入每个水果的操作 Consumer<String> printFruit = fruit -> System.out.println(fruit); fruits.forEach(printFruit); // 可简化为以下模式 fruits.forEach(System.out::println); }}打印后果: AppleOrangeBananaMango在下面的示例中,咱们首先创立了一个字符串列表 fruits,而后创立了一个 Consumer<String> 对象 printFruit,通过 lambda 表达式实现了 accept() 办法的具体操作,即打印每个水果的名称。 应用 forEach() 办法联合 Consumer 接口,能够简洁地遍历列表并执行指定的操作。 ...

July 7, 2023 · 1 min · jiezi

关于java:多线程知识三个线程如何交替打印ABC循环100次

本文博主给大家解说一道网上十分经典的多线程面试题目。对于三个线程如何交替打印ABC循环100次的问题。 下文实现代码都基于Java代码在单个JVM内实现。问题形容给定三个线程,别离命名为A、B、C,要求这三个线程依照程序交替打印ABC,每个字母打印100次,最终输入后果为: ABCABC...ABC举荐博主开源的 H5 商城我的项目waynboot-mall,这是一套全副开源的微商城我的项目,蕴含三个我的项目:经营后盾、H5 商城前台和服务端接口。实现了商城所需的首页展现、商品分类、商品详情、商品 sku、分词搜寻、购物车、结算下单、支付宝/微信领取、收单评论以及欠缺的后盾治理等一系列性能。 技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等罕用中间件。分模块设计、简洁易保护,欢送大家点个 star、关注博主。 github 地址:https://github.com/wayn111/waynboot-mall 解决思路这是一个典型的多线程同步的问题,须要保障每个线程在打印字母之前,可能判断是否轮到本人执行,以及在打印字母之后,可能告诉下一个线程执行。为了实现这一指标,博主讲介绍以下5种办法: 应用synchronized和wait/notify应用ReentrantLock和Condition应用Semaphore应用AtomicInteger和CAS应用CyclicBarrier办法一:应用synchronized和wait/notifysynchronized是Java中的一个关键字,用于实现对共享资源的互斥拜访。wait和notify是Object类中的两个办法,用于实现线程间的通信。wait办法会让以后线程开释锁,并进入期待状态,直到被其余线程唤醒。notify办法会唤醒一个在同一个锁上期待的线程。 咱们能够应用一个共享变量state来示意以后应该打印哪个字母,初始值为0。当state为0时,示意轮到A线程打印;当state为1时,示意轮到B线程打印;当state为2时,示意轮到C线程打印。每个线程在打印完字母后,须要将state加1,并对3取模,以便循环。同时,每个线程还须要唤醒下一个线程,并让本人进入期待状态。 具体的代码实现如下: public class PrintABC { // 共享变量,示意以后应该打印哪个字母 private static int state = 0; // 共享对象,作为锁和通信的媒介 private static final Object lock = new Object(); public static void main(String[] args) { // 创立三个线程 Thread threadA = new Thread(new Runnable() { @Override public void run() { try { // 循环100次 for (int i = 0; i < 100; i++) { // 获取锁 synchronized (lock) { // 判断是否轮到本人执行 while (state % 3 != 0) { // 不是则期待 lock.wait(); } // 打印字母 System.out.println("A"); // 批改状态 state++; // 唤醒下一个线程 lock.notifyAll(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { try { for (int i = 0; i < 100; i++) { synchronized (lock) { while (state % 3 != 1) { lock.wait(); } System.out.println("B"); state++; lock.notifyAll(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread threadC = new Thread(new Runnable() { @Override public void run() { try { for (int i = 0; i < 100; i++) { synchronized (lock) { while (state % 3 != 2) { lock.wait(); } System.out.println("C"); state++; lock.notifyAll(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }); // 启动三个线程 threadA.start(); threadB.start(); threadC.start(); }}办法二:应用ReentrantLock和ConditionReentrantLock是Java中的一个类,用于实现可重入的互斥锁。Condition是ReentrantLock中的一个接口,用于实现线程间的条件期待和唤醒。ReentrantLock能够创立多个Condition对象,每个Condition对象能够绑定一个或多个线程,实现对不同线程的准确管制。 ...

July 7, 2023 · 5 min · jiezi

关于java:Java-8函数式接口之BinaryOperator用法

BinaryOperator<T> 是 Java 8 中的一个函数式接口,是 BiFunction<T, T, T> 的子接口。它示意承受两个雷同类型的输出参数 T 并返回一个后果 T 的操作。以下是 BinaryOperator<T> 接口的用法示例: import java.util.function.BinaryOperator;public class BinaryOperatorExample { public static void main(String[] args) { // 示例1:对两个整数进行相加 BinaryOperator<Integer> add = (num1, num2) -> num1 + num2; int sum = add.apply(5, 3); System.out.println(sum); // 输入: 8 // 示例2:比拟两个字符串的长度并返回较长的那个 BinaryOperator<String> longerString = (str1, str2) -> str1.length() >= str2.length() ? str1 : str2; String result = longerString.apply("Hello", "World"); System.out.println(result); // 输入: Hello // 示例3:组合多个函数 BinaryOperator<Integer> multiplyAndAdd = (num1, num2) -> (num1 * 2) + (num2 * 3); sum = multiplyAndAdd.apply(2, 3); System.out.println(sum); // 输入: 13 }}在示例1中,咱们创立了一个 BinaryOperator<Integer> 对象 add,用于对两个整数进行相加。通过调用 apply 办法,并将数字 5 和 3 作为参数传入,咱们能够失去后果 8。 ...

July 7, 2023 · 1 min · jiezi

关于java:Java7-Reasons-to-Migrate-from-Java-8-to-Java-17

原文7 Reasons to Migrate from Java 8 to Java 17 | Level Up Coding (medium.com) IntroductionFrom Java 8 to Java 18, Java has come a long way, there have been a lot of changes in the Java ecosystem since Java 8. The most notable change is the release cadence of Java. Java 8 was released in 2014, and Java 17 got released in 2021. That’s a gap of 7 years between the two releases. But now Java is released every six months so that you can expect a new version of Java every six months. This is a massive change for the Java ecosystem, as it allows developers to use the latest features of Java without having to wait for a long time. ...

July 7, 2023 · 4 min · jiezi

关于java:JavaWe-are-living-in-a-Continuous-World

【Java】We are living in a Continuous World. Continuous Integration, Continuous Deployment, Continuous Learning, and so on.咱们正生存在一个继续的世界中。继续集成、继续部署、继续学习,等等。 Machines are learning faster and they are more consistent than us. They almost never fail. But we humans cannot be as consistent as machines. We tend to be lethargic and we have hiccups at times. But that’s perfectly fine and that’s the human nature人和机器竞争上,人往往会处于劣势。人会走神,有时候还会昏昏欲睡。 Sometimes we like to start learning new technologies but we may fail due to one of the following reasons ...

July 7, 2023 · 8 min · jiezi

关于java:Java-8函数式接口之UnaryOperator用法

UnaryOperator<T> 是 Java 8 中的一个函数式接口,是 Function<T, T> 的子接口。它示意承受一个输出参数和返回值都是雷同类型 T 的操作。以下是 UnaryOperator<T> 接口的用法示例: import java.util.function.UnaryOperator;public class UnaryOperatorExample { public static void main(String[] args) { // 示例1:对整数进行平方运算 UnaryOperator<Integer> square = num -> num * num; int result = square.apply(5); System.out.println(result); // 输入: 25 // 示例2:增加感叹号到字符串开端 UnaryOperator<String> addExclamation = str -> str + "!"; String text = addExclamation.apply("Hello"); System.out.println(text); // 输入: Hello! // 示例3:组合多个函数 UnaryOperator<Integer> incrementAndSquare = num -> num + 1; incrementAndSquare = incrementAndSquare.andThen(square); result = incrementAndSquare.apply(5); System.out.println(result); // 输入: 36 }}在示例1中,咱们创立了一个 UnaryOperator<Integer> 对象 square,用于对整数进行平方运算。通过调用 apply 办法,并将数字 5 作为参数传入,咱们能够失去后果 25。 ...

July 7, 2023 · 1 min · jiezi

关于java:Java-8函数式接口之Function用法

Function<T, R> 是 Java 8 中的一个函数式接口,用于示意承受一个输出参数 T,并返回一个后果 R 的函数。它定义了一个形象办法 R apply(T t),须要依据输出参数的类型来实现。以下是 Function<T, R> 接口的用法示例: import java.util.function.Function;public class FunctionExample { public static void main(String[] args) { // 示例1:将字符串转换为对应的整数 Function<String, Integer> strToInt = Integer::parseInt; int num = strToInt.apply("123"); System.out.println(num); // 输入: 123 // 示例2:将字符串转换为其长度 Function<String, Integer> strLength = String::length; int length = strLength.apply("Hello World"); System.out.println(length); // 输入: 11 // 示例3:组合多个函数 Function<String, Integer> strToIntAndMultiplyBy2 = strToInt.andThen(n -> n * 2); int result = strToIntAndMultiplyBy2.apply("5"); System.out.println(result); // 输入: 10 }}在示例1中,咱们创立了一个 Function<String, Integer> 对象 strToInt,将其定义为 Integer::parseInt 办法的援用。该 Function 实例能够将传入的字符串解析为相应的整数。通过调用 apply 办法,并将字符串 "123" 作为参数传入,咱们能够失去整数后果 123。 ...

July 7, 2023 · 1 min · jiezi

关于java:Java-8函数式接口之Predicate用法

在Java 8中,Predicate<T>是一个函数式接口,用于对某个类型的对象进行条件判断,返回一个布尔值。Predicate<T>接口定义如下: @FunctionalInterfacepublic interface Predicate<T> { boolean test(T t);}Predicate<T>接口只有一个形象办法 test,该办法承受一个泛型参数并返回一个布尔值。你能够应用Lambda表达式或办法援用来实现 Predicate<T> 接口,依据本人的需要编写条件判断代码。 以下是 Predicate<T> 简略的示例用法: Predicate<Integer> isEven = num -> num % 2 == 0;System.out.println(isEven.test(4)); // 输入: trueSystem.out.println(isEven.test(7)); // 输入: false上述示例创立了一个用于判断一个整数是否为偶数的 Predicate<Integer> 对象 isEven。通过调用 test 办法,你能够传入一个整数,而后失去对应的布尔值后果。 Predicate<T> 接口还能够与其余函数式接口联合应用,如 filter 办法配合 Stream 应用,进行元素的过滤操作。 以下是应用 Predicate<T> 进行元素过滤的示例: import java.util.Arrays;import java.util.List;import java.util.function.Predicate;import java.util.stream.Collectors;public class PredicateExample { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Predicate<Integer> isEven = num -> num % 2 == 0; Predicate<Integer> isGreaterThan5 = num -> num > 5; List<Integer> filteredNumbers = numbers.stream() .filter(isEven.and(isGreaterThan5)) .collect(Collectors.toList()); System.out.println(filteredNumbers); // 输入: [6, 8, 10] }}在上述示例中,咱们应用 filter 办法联合 Predicate<T> 对流中的元素进行过滤,只保留了偶数。最终将后果收集到一个新的列表中。 ...

July 7, 2023 · 2 min · jiezi

关于java:Java-8函数式接口之Supplier用法

Java 8引入了函数式接口的概念,其中之一是 Supplier 接口。 Supplier 接口是一个无参数的函数式接口,它不承受任何输出参数,但能够返回一个后果。它定义了一个名为 get() 的形象办法,用于获取后果。 上面是Supplier<T>接口的定义: @FunctionalInterfacepublic interface Supplier<T> { T get();}在函数式编程中, Supplier 接口罕用于提早计算或惰性求值。它能够用来示意一个供应商或提供者,用于生成或获取数据。当须要获取数据时,能够调用 Supplier 的 get() 办法来获取后果。 应用 Supplier 接口的次要劣势之一是它能够与Java 8的新个性lambda表达式联合应用。通过lambda表达式,能够以更简洁和易读的形式定义 Supplier 对象。 上面是一个应用Supplier接口的示例: import java.util.function.Supplier;public class SupplierExample { public static void main(String[] args) { // 应用lambda表达式创立Supplier对象 Supplier<String> supplier = () -> "你好,世界!"; String result = supplier.get(); // 调用get()办法获取后果 System.out.println(result); }}// 输入:你好,世界!在下面的示例中,咱们应用lambda表达式创立了一个返回字符串"Hello, World!"的 Supplier 对象。而后,咱们调用了 get() 办法来获取后果,并将其打印到管制台上。除了简略的返回固定值之外, Supplier 接口还能够用于生成随机数、读取文件、从数据库中获取数据等各种场景。通过实现 Supplier 接口的自定义类,咱们能够依据具体需要来生成或获取数据。以下是其余常见用法示例: 1. 提供随机数生成器: Supplier<Integer> randomGenerator = () -> new Random().nextInt();int randomNumber = randomGenerator.get();2. 提早加载资源: ...

July 7, 2023 · 1 min · jiezi

关于java:VSCode中打开NodeJS项目自动切换对应版本的配置

这几年搞了不少动态站点,有的是Hexo的,有的是VuePress的。因为不同的主题对于NodeJS的版本要求不同,所以本机上不少NodeJS的版本。 对于如何治理多个NodeJS版本,很早之前就写过用nvm来治理的相干文章,这里就不赘述了,有须要的能够看这篇Node.js环境搭建。 尽管有了多版本治理,然而默认版本只有一个,所以很多时候,在用VSCode关上不同我的项目的时候,还须要用nvm use来切换不同的版本应用。显然始终这样操作很麻烦,而且容易遗记什么我的项目用什么版本。 所以,最好就是能关上我的项目的时候,主动就切换到对应的NodeJS版本。 要实现这样的成果只须要上面两步: 第一步:装置VSCode插件vsc-nvm 第二步:在我的项目根目录下创立文件.nvmrc,文件内容为版本号,比方: v10.13.0实现配置后,敞开VSCode,再从新关上,能够看到终端主动关上,并执行了nvm use命令,实现了NodeJS版本的主动切换 好了,明天的分享就到这里,心愿对您有用。码字不易,欢送转载,但请附上本文链接~ 欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

July 7, 2023 · 1 min · jiezi

关于java:Java中如何使用日志库在代码中添加日志

在Java中,能够应用日志库来在代码中增加日志。常见的日志库包含Log4j、Logback和java.util.logging等。以下是一个示例,展现了如何在Java代码中增加日志: 1. 导入日志库:首先,将适宜你的我的项目的日志库增加到我的项目的依赖中。例如,如果你抉择应用Logback作为日志库,能够在Maven我的项目的pom.xml文件中增加以下依赖项: <dependencies> <!-- Logback --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency></dependencies>2. 创立Logger对象:在代码中,创立一个Logger对象来记录日志。通常,咱们应用类的全名作为Logger对象的名称。 import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class MyClass { private static final Logger logger = LoggerFactory.getLogger(MyClass.class); public void doSomething() { String name = "Alice"; int age = 30; logger.info("User {} is {} years old.", name, age); }}3. 应用Logger记录日志:找到你心愿增加日志的中央,应用Logger对象的办法来记录日志。 logger.debug("This is a debug message.");logger.info("This is an info message.");logger.warn("This is a warning message.");logger.error("This is an error message.", exception);在上述代码示例中,咱们应用了Logger对象的不同办法来记录不同级别的日志音讯。这些级别包含DEBUG、INFO、WARN和ERROR。你还能够应用占位符,例如User {} is {} years old.,来动静地向日志音讯中增加变量。 ...

July 6, 2023 · 1 min · jiezi

关于java:使用-openaijavasdk-整合-ChatGPT

举荐一套基于 SpringBoot 开发的全平台数据 (数据库管理工具) 性能比较完善,倡议下载应用: github.com/EdurtIO/datacap 目前曾经反对 40+ 多种数据源。国内首个利用 ChatGPT 到数据管理系统中我的项目。github 地址:https://github.com/devlive-community/openai-java-sdk 本文咱们次要讲述通过 openai-java-sdk 依赖整合拜访 OpenAi 提供的性能。 OpenAI Java SDK 次要 为 Java 开发人员提供方便易用的 SDK 来与 OpenAI 的 API 进行交互。 构建 maven 我的项目,并增加依赖<dependency> <groupId>org.devlive.sdk</groupId> <artifactId>openai-java-sdk</artifactId> <version>1.2.0</version></dependency>举荐将 1.2.0 替换为 LATEST,这样能够始终应用最新版本。 构建连贯客户端OpenAiClient client = OpenAiClient.builder() .apiKey(System.getProperty("openai.token")) .build();默认咱们只须要指定 OpenAi 提供的密钥即可。如果咱们须要应用第三方提供的 OpenAi 镜像,能够应用以下代码: OpenAiClient client = OpenAiClient.builder() .apiHost(System.getProperty("proxy.host")) .apiKey(System.getProperty("proxy.token")) .build();proxy.host 第三方服务提供的主机名 proxy.token 第三方服务提供的密钥 构建 CompletionsCompletionEntity configure = CompletionEntity.builder() .model(CompletionModel.TEXT_DAVINCI_003.getName()) .prompt("测试一下") .temperature(2D) .build();client.createCompletion(configure) .getChoices() .forEach(System.out::println);运行程序后,输出大略如下内容: ...

July 6, 2023 · 2 min · jiezi

关于java:BiConsumer-接口中两个重要的方法andThen-和-accept-如何使用

BiConsumer 接口提供了两个重要的办法:andThen() 和 accept():1. andThen()办法:用于组合两个 BiConsumer 实例,造成一个新的 BiConsumer 实例,该实例按程序执行这两个操作。 BiConsumer<String, Integer> biConsumer1 = (str, num) -> { // 第一个操作};BiConsumer<String, Integer> biConsumer2 = (str, num) -> { // 第二个操作};BiConsumer<String, Integer> combinedBiConsumer = biConsumer1.andThen(biConsumer2);// 应用组合后的 BiConsumer 执行操作combinedBiConsumer.accept("Hello", 5);在上述示例中,咱们别离创立了两个 BiConsumer 实例 biConsumer1 和 biConsumer2。而后,应用 andThen() 办法将这两个实例组合成一个新的 BiConsumer 实例 combinedBiConsumer。 组合后的 combinedBiConsumer 将先执行 biConsumer1 的操作,再执行 biConsumer2 的操作。 2. accept()办法:用于承受参数并执行操作。 BiConsumer<String, Integer> biConsumer = (str, num) -> { System.out.println("String: " + str); System.out.println("Integer: " + num);};String str = "Hello";int num = 5;biConsumer.accept(str, num);在上述示例中,咱们创立了一个 BiConsumer<String, Integer> 实例 biConsumer,它承受一个字符串和一个整数作为参数。在 accept() 办法中,咱们打印了传入的字符串和整数。 ...

July 6, 2023 · 1 min · jiezi

关于java:Java-8中BiConsumerR-T-接口用法

BiConsumer<R, T> 是一个函数式接口,它承受两个参数并且没有返回值。其中,R 和 T 是参数的类型。要应用 BiConsumer<R, T> 接口,能够依照以下步骤进行: 创立一个 BiConsumer<R, T> 的实例。BiConsumer<String, Integer> biConsumer = (str, num) -> { // 在这里执行操作};应用 accept() 办法来承受两个参数并执行操作。String str = "Hello";int num = 5;biConsumer.accept(str, num);在下面的示例中,咱们创立了一个 BiConsumer<String, Integer> 的实例 biConsumer,它承受一个字符串和一个整数作为参数。而后,咱们定义了在承受参数后执行的操作(在正文处)。最初,咱们别离传递一个字符串 "Hello" 和整数 5 给 biConsumer 的 accept() 办法进行操作。 请依据本人的具体需要替换 String 和 Integer 的类型,并在 accept() 办法中编写心愿执行的操作。

July 6, 2023 · 1 min · jiezi

关于java:如果catch异常处理代码块中包含了return语句那么finally还会执行吗

运行以下代码public static void main(String[] args) { System.out.println(test());}public static String test(){ try { int t = 1 / 0; System.out.println("============"); } catch (Exception e) { System.out.println("-------------"); return "exception"; } finally { System.out.println("++++++++++++"); } return "";}输入: D:\java1.8\jdk1.8.0_60\bin\java.exe ...Connected to the target VM, address: '127.0.0.1:49697', transport: 'socket'-------------++++++++++++exceptionDisconnected from the target VM, address: '127.0.0.1:49697', transport: 'socket'Process finished with exit code 0论断: 依据输入后果能够看出即便catch代码块中蕴含return也同样会执行finally代码块中的内容finally代码块执行结束后会继续执行catch代码块中的return语句 那么finally代码块中同样蕴含一个return语句又会怎么执行呢?public static void main(String[] args) { System.out.println(test());}public static String test() { try { int t = 1 / 0; System.out.println("============"); } catch (Exception e) { System.out.println("-------------"); return "exception"; } finally { System.out.println("++++++++++++"); return "***************"; }}输入: ...

July 6, 2023 · 1 min · jiezi

关于java:2023-年-7-个适合初学者的-Vuejs-教程

这个精心筛选的列表将帮忙 Vue 初学者找到七个很棒的资源来开始学习 Vue。我置信你来这里是为了寻找一些资源来开始学习 Vue.js 框架的微妙旅程,无论是作为第一个工具还是你相熟的其余框架的附加工具。不管怎样,你很侥幸,因为这就是咱们将在这篇文章中介绍的内容。随着古代 Web 应用程序对更多功能、复杂性、可拜访性和性能的需要一直减少,开发人员面临着更艰巨的工作,即交付满足古代用户需要的高质量 Web 应用程序。为了帮忙实现这一指标,咱们开发了通过三思而行的代码块,这些代码块提供了作为库和框架存在的统一且标准化的应用程序构造,以进一步加强开发人员的能力并简化将优良应用程序交付到生产环境中所波及的流程。多年来,曾经开发出了许多杰出的前端框架/库,每个框架/库都有其独特的形式来开展业务,为 Web 应用程序构建动静、数据驱动的用户界面。在这篇文章中,咱们将关注这一长串框架中的重要人物;依据StackOverflow 2021 年 Web 框架风行度统计,Facebook 的 React.js 排名第一,其次是 Google 的 Angular 和 Vue.js。乍一看,依据下面介绍的受欢迎水平统计数据和科技巨头的反对,你可能会想少见多怪地放弃 Vue.js,但尝试以这种形式解释统计数据可能会产生一些误导性的后果。你可能会感兴趣的是,尽管 Vue.js 创立的工夫较晚,但与较早呈现的另外两个相比,Vue.js 在 2014 年创立,在撰写本文时,Vue 领有最高的 GitHub 星数,为 190k,超过了 React 的 185k 和 Angular 的 80k 。这展现了 Vue.js 是如何倒退并在业界真正风行的。这三个框架都有其独特之处,但它们的架构有一些相似之处。家喻户晓,Vue.js 的外围联合了 React 和 Angular 的大部分最佳性能。Vue.js 应用虚构 DOM,这是使 React 疾速的相干性能之一。Vue.js 也是基于组件的,应用双向数据绑定并采纳申明式办法——Angular 和 React 共享的架构。因而,所有三个框架都遵循传统的 MVC 模式。Vue.js 还与 Angular 共享相似的模板语法。 先决条件在学习 Vue 之前,读者必须至多对三种外围 Web 技术——HTML、CSS 和 JavaScript 有肯定的理解。 ...

July 6, 2023 · 1 min · jiezi

关于java:一道经典面试题BeanFactory-和-FactoryBean-有何区别

这也是 Spring 面试时一道经典的面试问题,明天咱们来聊一聊这个话题。 其实从名字上就能看进去个一二,BeanFactory 是 Factory 而 FactoryBean 是一个 Bean,咱们先来看下总结: BeanFactory 是 Spring 框架的外围接口之一,用于治理和获取应用程序中的 Bean 实例。它是一个工厂模式的实现,负责创立、配置和治理 Bean 对象。BeanFactory 是 Spring IoC 容器的根底,它能够从配置元数据(如 XML 文件)中读取 Bean 的定义,并在须要时实例化和提供这些 Bean。FactoryBean 是一个非凡的 Bean,它是一个工厂对象,用于创立和治理其余 Bean 的实例。FactoryBean 接口定义了一种创立 Bean 的形式,它容许开发人员在 Bean 的创立过程中进行更多的自定义操作。通过实现 FactoryBean 接口,开发人员能够创立简单的 Bean 实例,或者在 Bean 实例化之前进行一些额定的逻辑解决。区别在于,BeanFactory 是 Spring 框架的外围接口,用于治理和提供 Bean 实例,而 FactoryBean 是一个非凡的 Bean,用于创立和治理其余 Bean 的实例。FactoryBean 在 Bean 的创立过程中提供更多的自定义能力,容许进行额定的逻辑解决。 可能有的小伙伴看的还不是很分明,咱们再来具体看下。 1. BeanFactoryBeanFactory 看名字就晓得这是一个 Bean 工厂,小伙伴们晓得,Spring IoC 容器帮咱们实现了 Bean 的创立、治理等操作,那么这些操作都离不开 BeanFactory。 咱们来简略看下 BeanFactory 的代码: ...

July 6, 2023 · 4 min · jiezi

关于java:Java中如何使用日志库在代码中添加日志

在Java中,你能够应用日志库来在代码中增加日志。常见的日志库包含Log4j、Logback和java.util.logging等。以下是一个示例,展现了如何在Java代码中增加日志: 导入日志库:首先,将适宜你的我的项目的日志库增加到我的项目的依赖中。例如,如果你抉择应用Logback作为日志库,能够在Maven我的项目的pom.xml文件中增加以下依赖项:<dependencies> <!-- Logback --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency></dependencies>创立Logger对象:在代码中,创立一个Logger对象来记录日志。通常,咱们应用类的全名作为Logger对象的名称。import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class MyClass { private static final Logger logger = LoggerFactory.getLogger(MyClass.class); public void doSomething() { String name = "Alice"; int age = 30; logger.info("User {} is {} years old.", name, age); }}应用Logger记录日志:找到你心愿增加日志的中央,应用Logger对象的办法来记录日志。logger.debug("This is a debug message.");logger.info("This is an info message.");logger.warn("This is a warning message.");logger.error("This is an error message.", exception);在上述代码示例中,咱们应用了Logger对象的不同办法来记录不同级别的日志音讯。这些级别包含DEBUG、INFO、WARN和ERROR。你还能够应用占位符,例如User {} is {} years old.,来动静地向日志音讯中增加变量。 配置日志记录器:依据你抉择的日志库,你可能须要在我的项目的配置文件中进行一些设置,如logback.xml或log4j.properties。通过配置文件,你能够指定日志输入的格局、输入指标以及其余行为。 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n </Pattern> </layout> </appender>在上述配置中,咱们定义了一个名为STDOUT的ConsoleAppender,用于将日志输入到控制台。通过PatternLayout和Pattern元素,指定了日志的格局。 ...

July 6, 2023 · 1 min · jiezi

关于java:Java内存泄露问题分析

最近有一个服务器,常常运行的时候就呈现过载宕机的景象。重启脚本和零碎后,该个问题还是会呈现。只管有大量的数据失落,但因不是要害业务,问题并 不重大。不过还是决定作进一步的考察,来看下问题到底呈现在哪。首先留神到的是,服务器通过了所有的单元测试和残缺的集成环境的测试。在测试环境下应用测 试数据时运行失常,那么为什么在生产环境中运行会呈现问题呢?很容易会想到,兴许是因为理论运行时的负载大于测试时的负载,甚至超过了设计的负荷,从而耗 尽了资源。然而到底是什么资源,在哪里耗尽了呢?上面咱们就钻研这个问题 为了演示这个问题,首先要做的是编写一些内存泄露的代码,将应用生产-消费者模式去实现,以便更好阐明问题。 例子中,假设有这样一个场景:假如你为一个证劵经纪公司工作,这个公司将股票的销售额和股份记录在数据库中。通过一个简略过程获取命令并将其寄存在一个队列中。另一个过程从该队列中读取命令并将其写入数据库。命令的POJO对象非常简略,如下代码所示: public class Order { private final int id; private final String code; private final int amount; private final double price; private final long time; private final long[] padding; /** @param idThe order id@param codeThe stock code@param amountthe number of shares@param pricethe price of the share@param timethe transaction time */public Order(int id, String code, int amount, double price, long time) { super(); this.id = id; this.code = code; this.amount = amount; this.price = price; this.time = time; //这里成心设置Order对象足够大,以不便例子稍后在运行的时候耗尽内存 this.padding = new long[3000]; Arrays.fill(padding, 0, padding.length - 1, -2); } ...

July 6, 2023 · 3 min · jiezi

关于java:美团面试真题和答案

问题来源于某客,如下图所示: 问题链接:https://www.nowcoder.com/feed/main/detail/b12f8ece42f6485d8e462ab872c4f8d8答案解析1.线程池有几种实现形式?线程池的创立办法总共有 7 种,但总体来说可分为 2 类: 通过 ThreadPoolExecutor 创立的线程池;通过 Executors 创立的线程池。 线程池的创立形式总共蕴含以下 7 种(其中 6 种是通过 Executors 创立的,1 种是通过 ThreadPoolExecutor 创立的): Executors.newFixedThreadPool:创立一个固定大小的线程池,可管制并发的线程数,超出的线程会在队列中期待;Executors.newCachedThreadPool:创立一个可缓存的线程池,若线程数超过解决所需,缓存一段时间后会回收,若线程数不够,则新建线程;Executors.newSingleThreadExecutor:创立单个线程数的线程池,它能够保障先进先出的执行程序;Executors.newScheduledThreadPool:创立一个能够执行提早工作的线程池;Executors.newSingleThreadScheduledExecutor:创立一个单线程的能够执行提早工作的线程池;Executors.newWorkStealingPool:创立一个抢占式执行的线程池(工作执行程序不确定)【JDK 1.8 增加】。ThreadPoolExecutor:最原始的创立线程池的形式,它蕴含了 7 个参数可供设置,会更加可控。 2.线程池的参数含意?问到线程池参数的含意,肯定是问 ThreadPoolExecutor 参数的含意,这七个参数的含意别离是: 个参数代表的含意如下: 参数 1:corePoolSize外围线程数,线程池中始终存活的线程数。 参数 2:maximumPoolSize最大线程数,线程池中容许的最大线程数,当线程池的工作队列满了之后能够创立的最大线程数。 参数 3:keepAliveTime最大线程数能够存活的工夫,当线程中没有工作执行时,最大线程就会销毁一部分,最终放弃外围线程数量的线程。 参数 4:unit:单位是和参数 3 存活工夫配合应用的,合在一起用于设定线程的存活工夫 ,参数 keepAliveTime 的工夫单位有以下 7 种可选: TimeUnit.DAYS:天TimeUnit.HOURS:小时TimeUnit.MINUTES:分TimeUnit.SECONDS:秒TimeUnit.MILLISECONDS:毫秒TimeUnit.MICROSECONDS:奥妙TimeUnit.NANOSECONDS:纳秒 参数 5:workQueue一个阻塞队列,用来存储线程池期待执行的工作,均为线程平安,它蕴含以下 7 种类型: ArrayBlockingQueue:一个由数组构造组成的有界阻塞队列;LinkedBlockingQueue:一个由链表构造组成的有界阻塞队列;SynchronousQueue:一个不存储元素的阻塞队列,即间接提交给线程不放弃它们;PriorityBlockingQueue:一个反对优先级排序的无界阻塞队列;DelayQueue:一个应用优先级队列实现的无界阻塞队列,只有在提早期满时能力从中提取元素;LinkedTransferQueue:一个由链表构造组成的无界阻塞队列。与SynchronousQueue相似,还含有非阻塞办法;LinkedBlockingDeque:一个由链表构造组成的双向阻塞队列。较罕用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 无关。 参数 6:threadFactory线程工厂,次要用来创立线程,默认为失常优先级、非守护线程。 参数 7:handler回绝策略,回绝解决工作时的策略,零碎提供了 4 种可选: AbortPolicy:回绝并抛出异样。CallerRunsPolicy:应用以后调用的线程来执行此工作。DiscardOldestPolicy:摈弃队列头部(最旧)的一个工作,并执行当前任务。DiscardPolicy:疏忽并摈弃当前任务。默认策略为 AbortPolicy。 3.锁降级的过程?锁降级的过程指的是 synchronized 锁降级的过程,synchronized 锁降级机制也叫做锁收缩机制,此机制诞生于 JDK 6 中。 ...

July 5, 2023 · 2 min · jiezi

关于java:Java中使用BigDecimal类进行金额的加减乘除运算

在Java中,能够应用BigDecimal类来进行金额的加减乘除运算。BigDecimal类提供了准确的数值计算,防止了浮点数运算的精度问题。 以下是应用BigDecimal进行金额加减乘除运算的示例代码: import java.math.BigDecimal;public class Main { public static void main(String[] args) { BigDecimal amount1 = new BigDecimal("10.50"); BigDecimal amount2 = new BigDecimal("5.25"); // 加法运算 BigDecimal sum = amount1.add(amount2); System.out.println("Sum: " + sum); // 减法运算 BigDecimal difference = amount1.subtract(amount2); System.out.println("Difference: " + difference); // 乘法运算 BigDecimal product = amount1.multiply(amount2); System.out.println("Product: " + product); // 除法运算 BigDecimal quotient = amount1.divide(amount2, 2, BigDecimal.ROUND_HALF_UP); System.out.println("Quotient: " + quotient); }}输入后果将会是: Sum: 15.75Difference: 5.25Product: 55.1250Quotient: 2.00在以上示例中,咱们创立了两个BigDecimal对象来示意金额,而后应用add()办法进行加法运算, subtract() 办法进行减法运算, multiply() 办法进行乘法运算, divide() 办法进行除法运算。须要留神的是,在进行除法运算时,咱们指定了保留小数点后两位,并应用 BigDecimal.ROUND_HALF_UP 进行四舍五入。通过应用BigDecimal类,咱们能够保障金额的计算精度,防止浮点数运算的精度问题。

July 5, 2023 · 1 min · jiezi

关于java:Java策略模式简单示例代码

以下是应用Java实现冒泡排序、抉择排序和疾速排序的示例代码,应用策略模式实现: 首先,定义一个策略接口 SortingStrategy,蕴含一个排序办法 sort: public interface SortingStrategy { void sort(int[] array);}而后,实现三个具体的策略类,别离代表不同的排序算法: public class BubbleSortStrategy implements SortingStrategy { @Override public void sort(int[] array) { int n = array.length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (array[j] > array[j + 1]) { int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } System.out.println("Sorting using bubble sort: " + Arrays.toString(array)); }}public class SelectionSortStrategy implements SortingStrategy { @Override public void sort(int[] array) { int n = array.length; for (int i = 0; i < n - 1; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } System.out.println("Sorting using selection sort: " + Arrays.toString(array)); }}public class QuickSortStrategy implements SortingStrategy { @Override public void sort(int[] array) { quickSort(array, 0, array.length - 1); System.out.println("Sorting using quick sort: " + Arrays.toString(array)); } private void quickSort(int[] array, int low, int high) { if (low < high) { int pivot = partition(array, low, high); quickSort(array, low, pivot - 1); quickSort(array, pivot + 1, high); } } private int partition(int[] array, int low, int high) { int pivot = array[high]; int i = low - 1; for (int j = low; j < high; j++) { if (array[j] < pivot) { i++; int temp = array[i]; array[i] = array[j]; array[j] = temp; } } int temp = array[i + 1]; array[i + 1] = array[high]; array[high] = temp; return i + 1; }}接下来,能够应用策略模式进行排序操作: ...

July 5, 2023 · 2 min · jiezi

关于java:对敏感操作的二次认证-详解-SaToken-二级认证

一、需要剖析在某些敏感操作下,咱们须要对已登录的会话进行二次验证。 比方代码托管平台的仓库删除操作,只管咱们曾经登录了账号,当咱们点击 [删除] 按钮时,还是须要再次输出一遍明码,这么做次要为了两点: 保障操作者是以后账号自己。减少操作步骤,避免误删除重要数据。这就是咱们本篇要讲的 —— 二级认证,即:在已登录会话的根底上,进行再次验证,进步会话的安全性。 Sa-Token 是一个轻量级 java 权限认证框架,次要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相干问题。Gitee 开源地址:https://gitee.com/dromara/sa-token本文将介绍在 SpringBoot 架构下,如何应用 Sa-Token 实现二级认证操作。 首先在我的项目中引入 Sa-Token 依赖: <!-- Sa-Token 权限认证 --><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version></dependency>注:如果你应用的是 SpringBoot 3.x,只须要将 sa-token-spring-boot-starter 批改为 sa-token-spring-boot3-starter 即可。 二、具体API在Sa-Token中进行二级认证非常简单,只须要应用以下API: // 在以后会话 开启二级认证,工夫为120秒StpUtil.openSafe(120); // 获取:以后会话是否处于二级认证工夫内StpUtil.isSafe(); // 查看以后会话是否已通过二级认证,如未通过则抛出异样StpUtil.checkSafe(); // 获取以后会话的二级认证残余无效工夫 (单位: 秒, 返回-2代表尚未通过二级认证)StpUtil.getSafeTime(); // 在以后会话 完结二级认证StpUtil.closeSafe(); 三、一个小示例一个残缺的二级认证业务流程,应该大抵如下: package com.pj.cases.up;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import cn.dev33.satoken.stp.StpUtil;import cn.dev33.satoken.util.SaResult;/** * Sa-Token 二级认证示例 * * @author kong * @since 2022-10-16 */@RestController@RequestMapping("/safe/")public class SafeAuthController { // 删除仓库 ---- http://localhost:8081/safe/deleteProject @RequestMapping("deleteProject") public SaResult deleteProject(String projectId) { // 第1步,先查看以后会话是否已实现二级认证 // 这个中央既能够通过 StpUtil.isSafe() 手动判断, // 也能够通过 StpUtil.checkSafe() 或者 @SaCheckSafe 来校验(校验不通过时将抛出 NotSafeException 异样) if(!StpUtil.isSafe()) { return SaResult.error("仓库删除失败,请实现二级认证后再次拜访接口"); } // 第2步,如果已实现二级认证,则开始执行业务逻辑 // ... // 第3步,返回后果 return SaResult.ok("仓库删除胜利"); } // 提供明码进行二级认证 ---- http://localhost:8081/safe/openSafe?password=123456 @RequestMapping("openSafe") public SaResult openSafe(String password) { // 比对明码(此处只是举例,实在我的项目时可拿其它参数进行校验) if("123456".equals(password)) { // 比对胜利,为以后会话关上二级认证,有效期为120秒,意为在120秒内再调用 deleteProject 接口都无需提供明码 StpUtil.openSafe(120); return SaResult.ok("二级认证胜利"); } // 如果明码校验失败,则二级认证也会失败 return SaResult.error("二级认证失败"); } // 手动敞开二级认证 ---- http://localhost:8081/safe/closeSafe @RequestMapping("closeSafe") public SaResult closeSafe() { StpUtil.closeSafe(); return SaResult.ok(); }}全局异样拦截器,对立返回给前端的格局,参考: ...

July 5, 2023 · 2 min · jiezi

关于java:华为云CodeArts-Check带你掌握代码检查技巧优化代码质量

HDC期间可参加华为开发者大会Check 新人抽奖流动,流动链接在文末。福利多多,快来参加! 本文作为华为云CodeArts Check代码查看服务的开箱操作领导文档,会分为如下五个维度进行介绍:产品介绍、后期筹备、开明CodeArts软件开发生产线、Check代码查看工作新建、五大场景应用领导。 欢送各位感兴趣的小伙伴体验试用:[[>>>>>CodeArts Check代码查看服务链接<<<<<]](https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&s... "[>>>>>CodeArts Check代码查看服务链接<<<<<]");大家也能够扫描二维码理解详情: 注:首次体验CodeArts软件开发生产线服务的小伙伴,请先依据文章内“注册CodeArts软件开发生产线”一节内容,开明好服务后体验应用哦。 1. 产品介绍品质是产品的生命线,软件开发事先预防的老本远低于预先补救。如果程序员在代码编写阶段可能及时修改软件缺陷和安全隐患,防止缺点流向后端,就能够无效晋升产品质量。 基于华为在自动化源代码动态查看方面的技术积攒与企业级利用教训,华为云CodeArts Check代码查看服务,推出六大个性,为用户提供代码格调、通用品质与代码平安危险等查看能力,并提供问题闭环解决、检查报告等性能,可一站式实现代码查看作业,将代码质量保证流动从原始的人工检视中解脱进去,帮忙用户高效守护软件品质和平安,助力客户商业胜利。 目前,华为云CodeArts Check不仅撑持了华为15万研发人员、日均扫描逾500亿行的代码查看工作,也广泛应用于能源、物流等企业,新闻媒体及宽广开发者,反对企业对立标准开发规范,标准研发过程,将品质流动和安全检查前移到代码开发阶段,为企业生产高质量且平安的软件保驾护航。 2. 后期筹备须要注册好华为云账号并实现实名认证。 2.1 注册账号关上[[>>>>>华为云官网<<<<<]](https://www.huaweicloud.com/ "[>>>>>华为云官网<<<<<]")单击页面右上角“注册”按钮,依据提示信息实现注册步骤查看受权信息,确认无误后,单击“受权并登录”勾选服务条款, 单击“开明”:零碎提醒开明胜利。具体的注册步骤能够查看这里[[>>>>>注册华为帐号并开明华为云<<<<<]](https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&s... "[>>>>>注册华为帐号并开明华为云<<<<<]"). 编辑 2.2 实名认证具体实名认证介绍与步骤能够查看这里:[[实名认证介绍]](https://support.huaweicloud.com/usermanual-account/account_au... "[实名认证介绍]"). 编辑 3. 开明CodeArts软件开发生产线小伙伴们在登录华为云账号之后,点击[[>>>>>CodeArts软件开发生产线服务链接<<<<<]](https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&s... "[>>>>>CodeArts软件开发生产线服务链接<<<<<]")进入服务首页: 编辑 点击“收费试用”按钮,进入服务页面里;因为是新用户/在该区域未开明服务,所以须要咱们抉择绝对应的版本,开明/购买后体验应用: 编辑 作为集体开发者体验试用,咱们只须要抉择根底版上面的“收费开明”即可: 编辑 在购买页面,抉择“我曾经浏览并批准 《CodeArts服务应用申明》”,点击下一步之后,在下个界面点击“去领取”; 编辑 编辑 领取胜利会跳出“领取胜利”弹窗,点击“确认付款”: 编辑 点击“返回软件开发平台控制台”按钮,CodeArts软件开发生产线服务就算开明胜利了: 编辑 4. Check代码查看工作新建开明好CodeArts软件开发生产线服务后,会主动跳到“总览”界面;抉择侧边栏的“代码查看”,之后点击右上角的“立刻应用”按钮就能够进入代码查看页面了: 编辑 或则能够点击[[>>>>>CodeArts Check代码查看服务链接<<<<<]](https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&s... "[>>>>>CodeArts Check代码查看服务链接<<<<<]")进入咱们CodeArts Check代码查看服务的介绍首页: 编辑 点击“立刻应用”按钮,进入服务外部: 编辑 首次进入服务内的小伙伴会发现还没有新建任何工作,不必急,咱们这次体验会采纳现有demo我的项目来扫描。导入现有demo我的项目的形式有3种,咱们首先须要先到代码托管服务界面。 形式一单击“一般新建”旁的下扩按钮,在扩大框中抉择“按模板新建”,进入“按模板新建”页面: ...

July 4, 2023 · 1 min · jiezi

关于java:Java中如何使用enum关键字定义一个枚举类

在Java中,能够应用enum关键字来定义一个枚举类。以下是一个残缺的三属性枚举类的代码示例: public enum ThreeAttributesEnum { /** * ATTRIBUTE1 */ ATTRIBUTE1("Value1", 1, "Description1"), /** * ATTRIBUTE2 */ ATTRIBUTE2("Value2", 2, "Description2"), /** * ATTRIBUTE3 */ ATTRIBUTE3("Value3", 3, "Description3"); private final String name; private final int value; private final String description; ThreeAttributesEnum(String name, int value, String description) { this.name = name; this.value = value; this.description = description; } public String getName() { return name; } public int getValue() { return value; } public String getDescription() { return description; }}在下面的代码中,咱们定义了一个名为ThreeAttributesEnum的枚举类,并且定义了三个枚举常量ATTRIBUTE1,ATTRIBUTE2和ATTRIBUTE3,每个枚举常量都有一个name、一个value和一个description属性。 ...

July 4, 2023 · 1 min · jiezi

关于java:Java基础语法九

class类润饰 - staticstatic 称为动态,能够用来润饰属性或者办法。 如果属性或者办法的后面加上了static,那么属性或者办法就变成了动态属性或者静态方法。 一般来说,属于实例对象的属性或者办法属于每个类实例化产生的实例化对象,因为每个对象在堆内存中互相独立,所以一个对象身上的属性或者办法与其余对象的属性或者办法也独立于彼此。 当给属性或者办法的身上加上static润饰之后,这个属性和办法将不再属于实例化对象而是属于类自身。 所有的通过类实例化失去的对象都能够通过类自身应用动态属性或者办法。 例如: public class Student { public String name; public int age; static String info = "测试动态属性";}应用这个类: public class Demo1 { public static void main(String[] args) { Student s1 = new Student(); // 属于对象的属性能够通过对象.属性来操作 s1.name = "张三"; s1.age = 20; System.out.println(s1.name + " -- " + s1.age); // 张三 -- 20 // 动态属性能够通过类来进行拜访 System.out.println(Student.info); // 测试动态属性 }} 在上图中,能够通过实例对象.动态属性的形式拜访和批改,之所以能够这样做,是因为实例对象在堆中,除了存储对象属性和办法以外,还存储了类的地址,当拜访一个动态属性时就会立即通过类的地址拜访类中的动态属性,从而对动态属性或者办法进行操作。 类变量(动态属性)的利用场景在开发中,如果某个数据只须要一份,且心愿可能被共享(拜访、批改),则该数据能够定义 成类变量来记住。 例如: ...

July 4, 2023 · 1 min · jiezi

关于java:Java-中break和continue的作用和区别分别怎么用

在Java中,break 和 continue 是用于管制循环语句执行流程的关键字。它们的作用和应用办法如下: break 关键字:作用:break 用于立刻终止以后循环或开关语句块的执行,并跳出该语句块。应用办法:在须要终止循环或开关语句块的中央应用 break 关键字。示例:for (int i = 0; i < 10; i++) { if (i == 5) { break; // 当 i 等于 5 时完结循环 } System.out.println(i);}上述代码将输入:0 1 2 3 4。 continue 关键字:作用:continue 用于跳过以后循环的残余代码,间接进入下一次循环的判断条件。应用办法:在须要跳过以后循环残余代码的中央应用 continue 关键字。示例:for (int i = 0; i < 10; i++) { if (i == 5) { continue; // 当 i 等于 5 时跳过本次循环 } System.out.println(i);}上述代码将输入:0 1 2 3 4 6 7 8 9。 ...

July 4, 2023 · 1 min · jiezi

关于java:如何使用ModelMapper库进行对象之间的属性映射

ModelMapper是一个Java库,用于实现Java对象之间的映射。它提供了一个简略而弱小的API,能够主动将一个对象的属性值复制到另一个对象中,而无需手动编写一一属性的赋值代码。以下是应用ModelMapper类的示例代码: 1.导入ModelMapper库:首先须要导入ModelMapper库,能够通过Maven或手动下载并增加到我的项目中。Maven依赖: <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>2.4.2</version></dependency>2.创立ModelMapper对象:创立一个ModelMapper对象,用于进行对象之间的映射。 import org.modelmapper.ModelMapper;// 创立ModelMapper对象ModelMapper modelMapper = new ModelMapper();定义源对象和指标对象:定义一个源对象和一个指标对象,这两个对象之间的属性映射将通过ModelMapper来实现。public class SourceObject { private String property1; private int property2; // 其余属性和办法}public class TargetObject { private String property1; private int property2; // 其余属性和办法}3.进行对象之间的映射:应用ModelMapper的map()办法将源对象的属性值映射到指标对象中。 SourceObject sourceObject = new SourceObject();sourceObject.setProperty1("value1");sourceObject.setProperty2(10);TargetObject targetObject = modelMapper.map(sourceObject, TargetObject.class);在上述代码中,ModelMapper将sourceObject对象的属性值映射到targetObject对象中,无需手动一一赋值。 须要留神的是,ModelMapper在进行对象映射时,会主动匹配属性名雷同且类型雷同的属性,并将源对象的属性值复制到指标对象中。如果源对象和指标对象的属性名不完全相同,能够应用ModelMapper的配置性能进行属性名的映射配置。 残缺的示例代码如下所示: import org.modelmapper.ModelMapper;public class SourceObject { private String property1; private int property2; // 其余属性和办法 // getter和setter办法}public class TargetObject { private String property1; private int property2; // 其余属性和办法 // getter和setter办法}public class Main { public static void main(String[] args) { // 创立ModelMapper对象 ModelMapper modelMapper = new ModelMapper(); // 创立源对象 SourceObject sourceObject = new SourceObject(); sourceObject.setProperty1("value1"); sourceObject.setProperty2(10); // 进行对象之间的映射 TargetObject targetObject = modelMapper.map(sourceObject, TargetObject.class); // 输入指标对象的属性值 System.out.println(targetObject.getProperty1()); // 输入:value1 System.out.println(targetObject.getProperty2()); // 输入:10 }}以上示例代码演示了如何应用ModelMapper库进行对象之间的属性映射。 ...

July 4, 2023 · 1 min · jiezi

关于java:BeanUtilscopyProperties方法如何使用

BeanUtils.copyProperties()办法是Apache Commons BeanUtils库中的一个办法,用于将一个Java对象的属性值复制到另一个Java对象中。该办法提供了一种简略且不便的形式来进行对象属性的复制,缩小了手动编写一一属性赋值的工作量。上面具体解释BeanUtils.copyProperties()办法的用法。 1.导入BeanUtils库:首先须要导入Apache Commons BeanUtils库,能够通过Maven或手动下载并增加到我的项目中。Maven依赖: <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version></dependency>2.导入所需的类:在Java文件中导入所需的类,包含BeanUtils类和源对象、指标对象的类。 import org.apache.commons.beanutils.BeanUtils;// 导入源对象和指标对象的类import com.example.SourceObject;import com.example.TargetObject;3.复制属性值:应用BeanUtils.copyProperties()办法进行属性值的复制。该办法接管两个参数:源对象和指标对象。它会主动匹配属性名雷同的属性,并将源对象的属性值复制到指标对象中。 SourceObject sourceObject = new SourceObject();sourceObject.setProperty1("value1");sourceObject.setProperty2(10);TargetObject targetObject = new TargetObject();try { BeanUtils.copyProperties(targetObject, sourceObject);} catch (Exception e) { e.printStackTrace();}在上述代码中,BeanUtils.copyProperties()办法将sourceObject对象的属性值复制到targetObject对象中。 须要留神的是,BeanUtils.copyProperties()办法只会复制属性值,不会复制对象的援用。如果源对象和指标对象有雷同的援用类型属性,复制后的指标对象的该属性将与源对象的该属性援用雷同的对象。如果须要深度复制对象,能够应用其余形式,如序列化和反序列化。 此外,BeanUtils.copyProperties()办法还提供了其余的用法,如能够通过传递一个Map对象作为源对象来复制属性值,能够通过传递一个指标对象的Class对象来创立指标对象并复制属性值等。具体的用法能够参考BeanUtils库的文档。 总结:BeanUtils.copyProperties()办法是一个不便的工具办法,用于将一个Java对象的属性值复制到另一个Java对象中。它简化了属性复制的过程,缩小了手动编写一一属性赋值的工作量。但须要留神的是,该办法只复制属性值,不复制对象的援用。

July 4, 2023 · 1 min · jiezi

关于java:记一次使用分布式锁遇到设计问题

这个问题预计大部分开发都会遇到,大部分人都会脱漏,遇到这样状况,你是否立马能找到问题源自一个需要,对一个接口进行幂等管制。过后实现思路,创立一个申明注解,标注参数的对象的字段作为幂等标记,应用注解作为切点,进行盘绕告诉管制对业务办法进行加强,如果幂等字段曾经存在,不调用业务办法。 代码实现定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Idempotent { /** * 惟一标识 * @return */ String uniqueKey();}定义切面@Component@Aspect@Slf4jpublic class IdempotentAspect { private String keyTemplate = "system:%s.%s:%s"; @Autowired private RedisDistributedLock distributedLock; @Pointcut("@annotation(com.xxx.xx.Idempotency)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint point) throws Throwable { String className = point.getTarget().getClass().getName(); MethodSignature joinPointObject = (MethodSignature) point.getSignature(); Method method = joinPointObject.getMethod(); Idempotency annotation = AnnotationUtils.getAnnotation(method, Idempotency.class); String field = annotation.uniqueKey(); Object[] args = point.getArgs(); String methodName = point.getSignature().getName(); if (ArrayUtils.isEmpty(args)) return point.proceed(); Object targetObject = args[0]; if (isNull(targetObject)) return point.proceed(); PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetObject.getClass(), field); if (isNull(propertyDescriptor)) { log.warn("找不到对象属性 {}", field); return point.proceed(); } Object fieldValue = propertyDescriptor.getReadMethod().invoke(targetObject); String key = format(keyTemplate,className, methodName, fieldValue.toString()); boolean lock = distributedLock.lock(key); if (lock) { //判断fieldValue 在数据库是否存在、并且写入数据库中 boolean canExecute = false; canExecute = findRecord(fieldValue); if (canExecute) { try { return point.proceed(); } finally { distributedLock.releaseLock(key); } } } //略 }}下面就是盘绕告诉具体实现,省略一些具体操作实现,获取注解的uniqueKey到执行办法的参数对象中获取field值,通过办法名+field值进行加锁,并发管制,依据数据库判断管制执行。很简略就实现对办法进行全局幂等管制,十分自信连自测都没有进行调用,间接提交到测试环境,后果啪啪打脸。在finally 解锁办法中抛出一个异样org.springframework.dao.InvalidDataAccessApiUsageException: Unexpected exception while processing command; nested exception is org.redisson.client.RedisException: Unexpected exception while processing command。这样的后果我是很懵逼的,没道理会在这里出问题的啊。我是不是应用错API,毕竟这个是公司封装的分布式工具,在看了一个其余提交的代码,应用形式并没有任何问题。这个异样是redis执行脚本谬误导致,我第一想法是解锁的lua执行出错了,找共事要了类库的源码。 ...

July 4, 2023 · 2 min · jiezi

关于java:Spring-FrameWork从入门到NB-Environment-Abstraction

Environment Abstraction,环境形象,指的是Spring通过Environment接口不便地从环境变量或配置文件中读取数据。 Spring容器的Environment接口蕴含两个重要概念或模块:profiles和properties。 profiles:有active或inactive两个状态,只有active状态的profiles指定的资源才会失效、或者才会被注入到Spring容器中。 properties指的是包含: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Map objects, and so on等在内的资源。 profiles和properties联合应用,能够实现:通过profiles指定在什么条件或环境下、哪一类或哪些资源文件或环境变量、参数会失效,或者哪些bean definition会被加载到Spring容器中。 比方,通过profiles别离指定开发环境、测试环境和生产环境的配置文件。 Bean Definition Profiles通过@Profile注解实现不同环境下加载不同的Bean,例如: @Configuration@Profile("development")public class StandaloneDataConfig { @Bean public DataSource dataSource() { ... }}@Profile能够反对操作符:! & |,别离代表逻辑运算符not and or。 比方:@Profile({"p1", "!p2"}),示意p1激活、或者p2没有被激活的状况下失效。 @Profiles作用在@Configuration下示意这个配置文件下的所有bean都遵循其约定,作用在@Bean、或@Component上则示意以后bean遵循其约定。 激活ProfileSpring反对通过以下形式激活Profiles: 编程式:Environment的setActiveProfiles办法配置spring.profiles.active参数:通过零碎环境变量、 JVM system properties、servlet context parameters...等能够通过:@Profile("default")形式指定默认Profile,Spring没有检测到上述任何形式指定Profiles的状况下,缺省设置失效。 上一篇 Spring FrameWork从入门到NB - BeanPostProcessor

July 3, 2023 · 1 min · jiezi

关于java:Java-8中引入的Optional类详解附示例代码

Optional类是Java 8中引入的用于解决可能为null的值的容器类。它提供了一组办法来防止显式的null检查和解决,从而缩小空指针异样的危险,并使代码更加简洁和可读。上面是对于如何应用Optional类的具体解说。 创立Optional对象:应用Optional.of(value):创立一个蕴含非null值的Optional对象。如果传入的value为null,将会抛出NullPointerException。应用Optional.ofNullable(value):创立一个蕴含指定值的Optional对象,即便传入的value为null。查看Optional对象中是否有值:应用isPresent()办法:判断Optional对象中是否存在非null值。如果存在,返回true;否则,返回false。获取Optional对象中的值:应用get()办法:获取Optional对象中的非null值。如果Optional对象中没有值,将会抛出NoSuchElementException异样。因而,在调用get()办法之前,最好先应用isPresent()办法进行查看。提供默认值:应用orElse(defaultValue)办法:如果Optional对象中有值,则返回该值;否则,返回提供的默认值defaultValue。应用orElseGet(supplier)办法:如果Optional对象中有值,则返回该值;否则,调用提供的Supplier函数生成一个默认值并返回。抛出异样:应用orElseThrow(exceptionSupplier)办法:如果Optional对象中有值,则返回该值;否则,应用指定的异样生成函数exceptionSupplier抛出一个异样。应用条件操作:应用ifPresent(consumer)办法:如果Optional对象中有值,则执行指定的操作consumer,否则不执行任何操作。应用filter(predicate)办法:如果Optional对象中有值,并且该值满足谓词predicate,返回一个蕴含该值的Optional对象;否则,返回一个空的Optional对象。应用map(function)办法:如果Optional对象中有值,则利用指定的函数function到该值,并返回一个蕴含后果的Optional对象;否则,返回一个空的Optional对象。应用flatMap(function)办法:相似于map()办法,然而要求函数function返回一个Optional对象。应用Optional类能够使代码更具可读性和健壮性。它能够帮忙咱们更好地解决可能为null的值,防止了繁琐的null检查和解决,提供了一种优雅的形式来解决可能存在的值或者提供默认值或者抛出异样。然而,须要留神的是,适度应用Optional可能会使代码变得复杂,因而咱们应该依据理论状况抉择适当的应用Optional。以下是一个应用Optional的代码示例: import java.util.Optional;public class OptionalExample { public static void main(String[] args) { String value = "Hello, Optional!"; // 创立Optional对象 Optional<String> optional = Optional.of(value); // 判断值是否存在 boolean isPresent = optional.isPresent(); System.out.println("Is value present? " + isPresent); // 获取值 String result = optional.get(); System.out.println("Value: " + result); // 如果值存在,执行操作 optional.ifPresent(val -> System.out.println("Length: " + val.length())); // 转换值 optional.map(val -> val.toUpperCase()) .ifPresent(upperCase -> System.out.println("Upper case value: " + upperCase)); // 如果值不存在,提供默认值 String defaultValue = optional.orElse("Default Value"); System.out.println("Default Value: " + defaultValue); // 如果值不存在,执行操作获取默认值 String computedValue = optional.orElseGet(() -> computeDefaultValue()); System.out.println("Computed Value: " + computedValue); // 如果值不存在,抛出异样 try { String resultOrThrow = optional.orElseThrow(() -> new RuntimeException("Value is required.")); System.out.println("Result: " + resultOrThrow); } catch (RuntimeException e) { System.err.println("Exception: " + e.getMessage()); } } private static String computeDefaultValue() { return "Computed Default Value"; }}打印后果: ...

July 3, 2023 · 1 min · jiezi

关于java:华为云CodeArts-Artifact-5大特性揭秘大型企业制品管理面纱

 华为云CodeArts Artifact用于治理源代码编译后的构建产物,反对Maven、npm、PyPI、Docker、NuGet等常见制品包类型。能够与本地构建工具和云上的继续集成、继续部署无缝对接,同时反对制品包版本治理、细粒度权限管制、平安扫描等重要性能,实现软件包生命周期治理,晋升公布品质和效率。 官网体验通道:https://auth.huaweicloud.com/authui/login.html?locale=zh-cn&service=https%3A%2F%2Fwww.huaweicloud.com%2Fproduct%2Fcloudrelease.html%3Futm_medium%3Dhdc#/login

July 3, 2023 · 1 min · jiezi

关于java:Java-8中提供的时间类型API与MySQL数据库中的数据类型之间的对应关系

Java 8提供的工夫类型API(java.time包)与MySQL数据库中的数据类型之间的对应关系如下: LocalDate:对应MySQL的DATE数据类型。用于示意日期,不蕴含工夫局部。在Java中,它由LocalDate类示意。LocalTime:对应MySQL的TIME数据类型。用于示意工夫,不蕴含日期局部。在Java中,它由LocalTime类示意。LocalDateTime:对应MySQL的DATETIME数据类型。用于示意日期和工夫,蕴含年、月、日、时、分和秒。在Java中,它由LocalDateTime类示意。Instant:对应MySQL的TIMESTAMP数据类型。用于示意准确到纳秒级的工夫戳,示意自1970年1月1日以来通过的秒数。在Java中,它由Instant类示意。OffsetDateTime:对应MySQL的DATETIME数据类型。用于示意带有偏移量的日期和工夫。在Java中,它由OffsetDateTime类示意。ZonedDateTime:对应MySQL的DATETIME数据类型。用于示意带有时区信息的日期和工夫。在Java中,它由ZonedDateTime类示意。须要留神的是,MySQL数据库的数据类型与Java 8工夫类型API之间的对应关系是基于常见的应用状况。在理论应用时,还须要依据具体的需要和数据库设计来抉择适合的工夫类型。同时,还须要留神不同工夫类型之间的转换和兼容性,以确保在Java应用程序与MySQL数据库之间正确存储和检索日期和工夫数据。 此外,还有一些其余MySQL数据类型,如TIMESTAMP(6)、TIME(6)和DATETIME(6),它们能够用于存储更高精度的工夫数据,准确到纳秒级别。对应的Java 8工夫类型能够应用java.time包中的LocalDateTime、LocalTime和Instant来示意。

July 3, 2023 · 1 min · jiezi

关于java:LocalDateTime与Date类型间转化

将LocalDateTime转换为Date: LocalDateTime localDateTime = LocalDateTime.now();Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());将Date转换为LocalDateTime: Date date = new Date();LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

July 3, 2023 · 1 min · jiezi

关于java:DateTimeFormatter和SimpleDateFormat有什么区别

DateTimeFormatter和SimpleDateFormat都是用于日期工夫格式化和解析的类,但它们有以下几个区别: 1.线程安全性: DateTimeFormatter 是线程平安的,能够在多线程环境下共享和重用。SimpleDateFormat 不是线程平安的,不倡议在多线程环境中共享和重用,除非应用适当的同步措施。2.API 设计: DateTimeFormatter 是 Java 8 中引入的,并遵循了新的日期工夫 API 的设计准则,提供了统一且易于应用的办法,反对更多的日期工夫模式符号和灵便的格式化选项。SimpleDateFormat 是晚期的 Java 日期工夫 API(java.util.Date) 中的类,应用较老的设计格调,并且其 API 比拟无限。3.底层实现: DateTimeFormatter 应用基于解析器和格式化器的模型,底层应用 java.time.format.DateTimeFormatterBuilder 类来构建格式化模式。SimpleDateFormat 应用基于模式字符串的形式进行格式化和解析,底层应用 java.text.SimpleDateFormat 类。以下是应用DateTimeFormatter和SimpleDateFormat的代码示例: 应用DateTimeFormatter示例代码: import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;public class DateTimeFormatterExample { public static void main(String[] args) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 格式化日期工夫 LocalDateTime now = LocalDateTime.now(); String formattedDateTime = formatter.format(now); System.out.println("格式化后的日期工夫:" + formattedDateTime); // 解析日期工夫 String dateTimeString = "2023-07-03 12:30:45"; LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeString, formatter); System.out.println("解析后的日期工夫:" + parsedDateTime); }}打印后果: ...

July 3, 2023 · 1 min · jiezi

关于java:Java-8引入了哪些全新的日期和时间API

Java 8引入了全新的日期和工夫API,即java.time包,提供了用于解决日期、工夫和工夫距离的类。以下是一些示例: LocalDate:示意一个日期,例如:2023-07-03。 LocalDate date = LocalDate.now(); // 获取以后日期int year = date.getYear(); // 获取年份int month = date.getMonthValue(); // 获取月份int day = date.getDayOfMonth(); // 获取日期LocalTime:示意一个工夫,例如:12:30:45。 LocalTime time = LocalTime.now(); // 获取以后工夫int hour = time.getHour(); // 获取小时int minute = time.getMinute(); // 获取分钟int second = time.getSecond(); // 获取秒数LocalDateTime:示意日期和工夫,例如:2023-07-03T12:30:45。 LocalDateTime dateTime = LocalDateTime.now(); // 获取以后日期和工夫int year = dateTime.getYear(); // 获取年份int month = dateTime.getMonthValue(); // 获取月份int day = dateTime.getDayOfMonth(); // 获取日期int hour = dateTime.getHour(); // 获取小时int minute = dateTime.getMinute(); // 获取分钟int second = dateTime.getSecond(); // 获取秒数Period:示意日期之间的距离。 ...

July 3, 2023 · 1 min · jiezi

关于java:Java-8中DateTimeFormatter类如何使用

在Java 8中,能够应用DateTimeFormatter类来将日期格局转换为指定的格局(例如:yyyy-MM-dd HH:mm:ss)。 以下是将日期格局转换为指定格局的示例代码: import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;public class DateTimeFormatExample { public static void main(String[] args) { // 获取以后日期和工夫 LocalDateTime dateTime = LocalDateTime.now(); // 定义日期工夫格局 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 将日期工夫依照指定格局进行格式化 String formattedDateTime = dateTime.format(formatter); // 打印格式化后的日期工夫 System.out.println("Formatted DateTime: " + formattedDateTime); }}// 输入:Formatted DateTime: 2023-07-03 11:11:00在上述代码中,首先获取以后的日期和工夫LocalDateTime.now(),而后通过DateTimeFormatter.ofPattern()办法定义日期工夫的格局,参数为指定的格局字符串("yyyy-MM-dd HH:mm:ss")。接下来,应用format()办法将日期工夫对象依照指定的格局进行格式化,返回一个格式化后的字符串。最初,打印格式化后的日期工夫。 除了上述的格局字符外,还能够应用其余字符来示意日期工夫的分隔符,例如:-、/、:等。 须要留神的是,DateTimeFormatter是线程平安的,因而能够在多线程环境下共享和重用同一个DateTimeFormatter对象。 如何将DateTimeFormatter格式化的字符串转类型日期转换成Date类型? 能够应用DateTimeFormatter和LocalDateTime互相转换,而后再转换为Date格局。 // 1.将DateTimeFormatter格式化的日期字符串转换为LocalDateTime对象LocalDateTime localDateTime = LocalDateTime.parse(formattedDateTime, formatter);// 2.将LocalDateTime转换为Instant对象Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();// 3.将Instant对象转换为Date对象Date date = Date.from(instant);// 输入:Mon Jul 03 13:42:08 CST 2023ZoneId.systemDefault()是一个静态方法,用于获取零碎默认的时区。atZone()办法承受一个ZoneId参数,用于指定时区。它将LocalDateTime对象与指定的时区进行关联,并返回一个在该时区下的ZonedDateTime对象。Date.from()办法是将Instant对象转换为Date对象的静态方法。 ...

July 3, 2023 · 1 min · jiezi

关于java:Java-8中处理日期时间的API和方法atZoneZoneIdofInstant类Datefrom

1.atZone()是LocalDateTime类的一个办法,用于将日期工夫与指定的时区关联起来,返回一个ZonedDateTime对象。该办法承受一个ZoneId参数,用于指定时区。它将LocalDateTime对象与指定的时区进行关联,并返回一个在该时区下的ZonedDateTime对象。 以下是atZone()办法的示例用法: import java.time.LocalDateTime;import java.time.ZoneId;import java.time.ZonedDateTime;public class AtZoneExample { public static void main(String[] args) { // 获取以后日期和工夫 LocalDateTime dateTime = LocalDateTime.now(); // 指定时区为纽约 ZoneId zoneId = ZoneId.of("America/New_York"); // 将日期工夫与指定时区关联 ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); // 打印关联后的日期工夫 System.out.println("ZonedDateTime: " + zonedDateTime); }// 输入:2023-07-03T14:11:41.610-04:00[America/New_York]}在上述代码中,咱们首先获取以后的日期和工夫LocalDateTime.now()。而后,应用ZoneId.of()办法创立一个ZoneId对象,指定时区为纽约("America/New_York")。接下来,应用atZone()办法将日期工夫与指定时区关联,返回一个在纽约时区下的ZonedDateTime对象。最初,打印关联后的日期工夫。 须要留神的是,atZone()办法只能用于LocalDateTime对象,而不能用于LocalDate或LocalTime对象。如果须要将LocalDate或LocalTime与时区关联,能够先将其转换为LocalDateTime对象,而后再应用atZone()办法进行关联。 2.ZoneId.systemDefault()是一个静态方法,用于获取零碎默认的时区。该办法会返回一个代表以后零碎默认时区的ZoneId对象。零碎默认时区是依据操作系统的设置来确定的,它反映了以后零碎所在的地理位置的时区设置。 以下是ZoneId.systemDefault()办法的示例用法: import java.time.ZoneId;public class SystemDefaultZoneExample { public static void main(String[] args) { // 获取零碎默认时区 ZoneId defaultZone = ZoneId.systemDefault(); // 打印零碎默认时区 System.out.println("System Default Zone: " + defaultZone); }}在上述代码中,咱们应用ZoneId.systemDefault()办法获取零碎默认的时区。而后,通过打印输出,咱们能够查看零碎默认时区的标识符。 ...

July 3, 2023 · 1 min · jiezi

关于java:什么是分布式系统

要了解分布式系统,次要须要明确一下2个方面: 1.分布式系统肯定是由多个节点组成的零碎。 其中,节点指的是计算机服务器,而且这些节点个别不是孤立的,而是互通的。 2.这些连通的节点上部署了咱们的节点,并且互相的操作会有协同。 分布式系统对于用户而言,他们面对的就是一个服务器,提供用户须要的服务而已,而实际上这些服务是通过背地的泛滥服务器组成的一个分布式系统,因而分布式系统看起来像是一个超级计算机一样。 例如淘宝,平时大家都会应用,它自身就是一个分布式系统,咱们通过浏览器拜访淘宝网站时,这个申请的背地就是一个宏大的分布式系统在为咱们提供服务,整个零碎中有的负责申请解决,有的负责存储,有的负责计算,最终他们互相协调把最初的后果返回并出现给用户。应用分布式系统次要有特点:1.增大零碎容量。 咱们的业务量越来越大,而要能应答越来越大的业务量,一台机器的性能曾经无奈满足了,咱们须要多台机器能力应答大规模的利用场景。所以,咱们须要垂直或是程度拆分业务零碎,让其变成一个分布式的架构。 2.增强零碎可用。 咱们的业务越来越要害,须要进步整个零碎架构的可用性,这就意味着架构中不能存在单点故障。这样,整个零碎不会因为一台机器出故障而导致整体不可用。所以,须要通过分布式架构来冗余零碎以打消单点故障,从而进步零碎的可用性。 3.因为模块化,所以 零碎模块重用度更高 4.因为软件服务模块被拆分,开发和公布速度能够并行而变得更快 5.零碎扩展性更高 6.团队合作流程也会失去改善 分布式系统的类型有三种: 1.分布式解决,但只有一个总数据库,没有部分数据库 2.分层式解决,每一层都有本人的数据库 3.充沛扩散的分布式网络,没有地方管制局部,各节点之间的联系方式又能够有多种,如涣散的联接,严密的联接,动静的联接,播送告诉式的联接等 什么是 JAVA 分布式应用? 一个大型的零碎往往被分为几个子系统来做,一个子系统能够部署在一台机器的多个 JVM 上,也能够部署在多台机器上。然而每一个零碎不是独立的,不是齐全独立的。须要互相通信,独特实现业务性能。 一句话来说:分布式就是通过计算机网络将后端工作散布到多台主机上,多个主机一起协同实现工作。 实现分布式次要的形式 分布式应用用到的技术: 网络通信,基于音讯形式的零碎间通信和基于近程调用的零碎间通信。 毛病: 就是会减少技术的复杂度。 基于音讯的零碎通信形式,次要是利用的网络协议,比方 TCP/IP 协定。 零碎间的通信还须要对数据进行解决,比方同步 IO 和异步 IO。 近程调用实现零碎间的通信:通过调用本地的java接口的办法来通明的调用近程java的实现。具体的细节有框架来实现。参考(daxiacode dian com)图灵Java架构师6期面试题一、架构师的日常职责是什么 ?总体而言,架构师负责软件畛域的顶层设计。 架构师须要依据公司的倒退,布局企业将来若干年的架构,制订可落地的架构计划,解决技术难题,做技术选型与攻关,落地具体的架构。优良的架构师既能做架构计划,也能写具体的架构代码。 二、开发工程师和架构师有何区别?工作重点不同:架构师重点在于后期的架构布局,须要制订可落地的架构计划,联合公司的业务场景、团队的技术水平等因素做技术选型,解决技术难题等等;而开发工程师重点在于具体的落地,特地的, 开发工程师的工作重点落地具体的性能。 能力要求不同:架构师要求比拟高,要有架构的广度、深度,须要把握一系列的架构技术栈,要求有架构实战经验,要有很强的系统分析、零碎架构、零碎设计的能力。 开发工程师次要是要求相熟根本的技术栈,相熟相干业务,疾速落地产品的相干性能。 三、业务架构师与根底架构师区别对于java程序猿而言,架构师分为业务架构师,根底架构师两大类,从高级开发转成业务架构师,难度小,出问题快。业务架构和基础架构有70%是一样的,那就是都要求有架构能力,剩下的30%是业务架构要求熟练掌握业务,制订架构计划,架构落地,基础架构则是100%要求纯技术。短期而言,看似基础架构更景色,其实不然。业务架构发展前景更好一些,因为35岁当前,拼的是综合能力,不再是纯架构能力。业务架构要求有更好的沟通能力,架构布局,架构落地能力,肯定的行业业务背景,甚至治理能力,所以从业务架构更容易做到技术总监或cto。如果始终做基础架构,那么可能是首席架构师。个别的架构老司机是业务架构,基础架构通吃的,好待业,到什么山唱什么歌。 四、 UML对系统架构重不重要?UML是架构基本功,但又容易被开发童鞋漠视。架构师要有很强的系统分析,零碎架构,零碎设计,架构表达能力,通过把握UML,进步这些能力。业务架构师 通过 UML能够形象出业务平台的外围用例,能够把简单的业务流程以分析模型表白分明,高阶设计阶段,利用包图,组件图,部署图把设计,部署表白分明。根底架构师设计中间件,能够画uml合作图,或流动图表白技术性能的流程,设计阶段,能够画包图,表白各个包的性能,而后多人能够一起撸技术中间件的具体代码,做具体架构落地。 五、Springcloud和Dubbo用哪个?Dubbo相对而言,成熟稳固,文档齐全门槛低一些,然而很多服务治理方面的性能是缺失的。Springcloud大而全,但很多性能不弱小,不成熟。长期而言,集体更看好Spring cloud,尽管目前还有一些坑,而且门槛也比Dubbo高,但整体发展趋势比Dubbo强,Spring cloud生态体系比Dubbo更好,性能更全面。把握Dubbo和Spring cloud是不抵触的,二者有很多雷同的中央,又有很多中央不同。 六、分布式定时工作和个别的工作都什么区别?分布式定时工作个别是多台服务器能够同时跑定时工作,效率要比个别的工作高,可用性要比个别的工作高(能够做生效转移,架构上没有单点问题,工作节点能够监控),性能要比个别工作的强(架构是强伸缩性,多台机器一起运行,执行工夫要短),反对的并发能力远远超过个别的工作(多台机器执行,能够把海量数据调配给不同的机器执行,并发能力十分好)。 七、高并发和高性能的区别和分割是什么?简略而言,高并发是拜访数量,高性能是拜访响应工夫,两个不同的角度。 并发量化的常见参数指标,qps,tps等等,性能量化指标个别是解决工夫,比方:接口响应工夫是10ms和5分钟,性能是齐全不一样的。qps为100和qps为50万的并发架构齐全不一样。如果架构不合理,并发量越大,性能越差。如果架构正当,并发量的大小对性能根本没影响,加机器即可,软件架构不须要任何扭转。 八、reactor线程指的是reactor模型中的哪个局部?这个问题自身是有问题的。 reactor线程模型分为单线程,多线程,主从多线程。 理论编程过程中,第二种用的是最多的, 九、消息中间件目前应用频率最大是RabbitMQ吗?技术选型是从技术的应用场景,优缺点等方面综合评估的。很多企业用RocketMQ和kafka,大数据根本100%选kafka. 十、为什么要用spring?spring可能很好的和各大框架整合spring 通过IOC容器治理了对象的创立和销毁 工厂模式在应用hiberna,mybatis的时候,不必每次都编写提交的事务的代码,能够应用spring的AOP来治理事务 AOP其实就是一个动静代理的实现申明式事务和编程式事务。 十一、mybatis的优缺点?长处:SQL写在XML中,便于对立治理和优化提供映射标签,反对对象和数据库的orm字段关系映射能够对SQL进行优化。 毛病: SQL工作量大mybagtis移植姓不好不反对级联。 ...

July 3, 2023 · 1 min · jiezi

关于java:狂收-3K-Star一个高性能无侵入的-Java-性能监控和统计工具十分强悍

背景随着所在公司的倒退,应用服务的规模不断扩大,原有的垂直利用架构已无奈满足产品的倒退,几十个工程师在一个我的项目里并行开发不同的性能,开发效率一直升高。 于是公司开始全面推动服务化过程,把团队内的大部分工程师次要精力全副都集中到服务化中。服务化能够让每个工程师仅在本人负责的子项目中进行开发,进步了开发的效率,然而服务化同时也带来了其余问题: 无奈晓得每个服务的运行状况,例如,某一台服务它目前的 QPS 是多少?它的均匀提早是多少,99% 的提早是多少,99.9% 的提早又是多少?某一个接口响应工夫慢,如何定位是哪个办法引起的?每个服务的负载是否平衡?当服务呈现抖动时,如何判断是 DB、Cache 还是上游服务引起的?DB 和 Cache 响应提早是多少?如何评估服务的容量,随着服务的调用量越来越大,这个服务须要多少机器来撑持?什么时候应该加机器?针对以上开发中的懊恼,明天咱们介绍一个针对高并发、低提早利用设计的高性能 Java 性能监控和统计工具——MyPerf4J 。举荐一个开源收费的 Spring Boot 实战我的项目: https://github.com/javastacks/spring-boot-best-practice应用场景在开发环境中疾速定位 Java 应用程序的性能瓶颈在生产环境中长期监控 Java 应用程序的性能指标个性高性能: 单线程反对每秒 1000万次 响应工夫的记录,每次记录只破费 73纳秒无侵入: 采纳 JavaAgent 形式,对应用程序齐全无侵入,无需批改利用代码低内存: 采纳内存复用的形式,整个生命周期只产生极少的长期对象,不影响应用程序的GC高精度: 采纳纳秒来计算响应工夫高实时: 反对秒级监控,最低 1 秒!监控指标MyPerf4J 为每个利用收集数十个监控指标,所有的监控指标都是实时采集和展示的。 上面是 MyPerf4J 目前反对的监控指标列表: MethodRPS,Count,Avg,Min,Max,StdDev,TP50, TP90, TP95, TP99, TP999, TP9999, TP99999, TP100 MemoryHeapInit,HeapUsed,HeapCommitted,HeapMax,NonHeapInit,NonHeapUsed,NonHeapCommitted,NonHeapMax JVM GCCollectCount,CollectTime JVM ClassTotal,Loaded,Unloaded 疾速启动MyPerf4J 采纳 JavaAgent 配置形式,透明化接入利用,对利用代码齐全没有侵入。 下载下载并解压 MyPerf4J-ASM.zip浏览解压出的 README 文件批改解压出的 MyPerf4J.properties 配置文件中 app_name、metrics.log.xxx 和 filter.packages.include 的配置值MyPerf4J-ASM.zip包 :https://github.com/LinShunKang/Objects/blob/master/zips/CN/MyPerf4J-ASM-3.3.0-SNAPSHOT.zip?raw=true配置在 JVM 启动参数里加上以下两个参数 ...

July 3, 2023 · 1 min · jiezi

关于java:超简单零衍系统上线用户数据无缝切换

“零衍”系统管理平台(KINGPAAS,以下简称“零衍”),在宏大的技术生态中自成一家,致力于提供对立登录认证和用户数据聚合的服务。咱们领有一整套齐备的功能模块,包含对立门户治理、对立域名治理、多种单点协定、基础架构治理,以及对立服务治理等。其中,用户数据的聚合是咱们可能实现对立登录的关键所在。在本篇文章中,咱们次要向您展现当您的零碎环境胜利接入零衍后,如何初始化存量或新增的企业、用户数据。 当您打算将曾经在应用的零碎环境接入零衍时,可能曾经存在大量的账户信息,这就须要将这些用户数据初始化到零衍中,让零衍成为数据的管理中心。您能够抉择以下两种形式进行数据初始化:01Excel导入您能够将原环境的数据导出并整顿,而后依照零衍的规范模板将这些数据导入到零衍中。导入时,尽管零衍中的企业、用户数据与指标环境租户、账号数据还没有建设关联,但通过将企业数据散发至指标环境,即可实现关联。02调度工作利用HZERO的调度工作性能,您能够不便地进行企业数据的初始化和同步。您只需在指标环境保护好特定的调度工作,而后手动执行即可(详情可参考零衍最佳实际手册)。咱们提供了灵便的同步选项,包含同步的租户抉择和同步类型的设定。您能够全量同步用户数据,也能够抉择只同步未关联的或已关联的用户数据。 当您为新客户上线产品时,如果客户租户下还没有用户数据,咱们建议您先在零衍中创立数据,而后散发至指标环境,进行数据同步。具体操作形式有:01手动创立通过手动创立用户,数据创立后会主动散发至指标环境,实现新增数据的初始化。 02Excel导入如果须要批量创立新用户,您也能够将用户数据填入零衍的规范Excel模板中,而后导入到零衍零碎中,实现用户的初始化。 如果您的企业应用LDAP来治理员工账号,您能够在零衍内配置企业的LDAP服务器信息,而后将LDAP中的用户同步到零衍中并实现初始化。同时,咱们也反对配置定时同步工作,以实现实时更新用户信息。综合以上步骤,无论是已有的企业用户数据,还是新增的数据,零衍都为您提供了多种数据初始化的形式,以便于企业疾速、不便地治理用户数据,为实现对立单点认证奠定了松软的根底。 操作手册:汉得焱牛开放平台 文档>利用产品>零衍系统管理平台KINGPAAS分割邮箱:openhand@vip.hand-china.com试用零衍治理平台期间,若您有任何问题须要征询,都可返回焱牛开放平台(open.hand-china.com)提反馈,或将疑难发往分割邮箱,咱们有业余人员针对您的问题进行解决回复,有技术精良的研发团队依据您的反馈进行利用优化。期待您的反馈,咱们将用心看待每一份回应。 立刻申请收费试用吧!零衍系统管理平台KINGPAAS 更多精彩内容,扫码关注“四海汉得”公众号举荐浏览

July 3, 2023 · 1 min · jiezi

关于java:Java程序性能分析开篇之jps

一、前言开发Java我的项目过程中,难免会碰到一些 性能 问题,这时候就须要一些工具,帮忙排查本文开篇次要介绍 JDK自带的工具 jps,获取 Java程序列表,以 openjdk 11.0.10 为例二、Java程序列表:jps1. 简介用来查找以后用户的 Java 过程,而不能查找以后零碎中其余用户的过程相比 Linux零碎 的 ps -ef | grep java,Windows零碎的 tasklist | findstr java,jps 查找Java过程命令更简洁列表外面会多一个 Jps的过程,每次过程号都不一样 2. jps:输入 过程号、利用主类名不蕴含包名,有些类名不容易分辨是哪个服务的过程,如下第3个 Launcher 15056 31504 RemoteMavenServer3617604 Launcher11368 32764 Jps3. jps -l:多输入 包名输入包名,能帮忙分辨 是哪个服务的过程,如下第3个是 IDEA开发工具的 Launcher 15056 31504 org.jetbrains.idea.maven.server.RemoteMavenServer3617604 org.jetbrains.jps.cmdline.Launcher32324 jdk.jcmd/sun.tools.jps.Jps113684. jps -m:多输入 jar 门路相比 jps,多输入 jar 门路 5. jps -v:多输入 启动参数相比 jps,多输入 启动参数 6. jps -V:输入 flag 文件的 JVM 参数输入通过 flag 文件传递到 JVM 中的参数(很少用到).hotspotrc 文件或 - XX:Flags = 所指定的文件。没有配置时,成果和 jps 一样7. jps -q:只输入 过程号只输入 过程号,比拟实用于 docker、k8s容器等 只有1个Java过程的场景然而还有一个 jps过程号烦扰,并且只有过程号,不好辨别哪个是 jps过程 ...

July 3, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-BeanPostProcessor

Spring框架之所以NB,BeanPostProcessor功不可没。BeanPostProcessor通过生命周期回调实现对Bean的加强比方,其实Spring的AOP性能次要就是通过BeanPostProcessor实现的,以及,Spring重要的主动拆卸性能,也是通过BeanPostProcessor实现的。 BeanPostProcessor失效原理BeanPostProcessor指的是Spring容器中所有实现了BeanPostProcessor接口的类,通过如下形式失效: BeanPostProcessor注册:所有实现了BeanPostProcessor接口的类,在Spring初始化过程中注册到Spring容器中Bean生命周期的初始化前、后,Spring别离回调BeanPostProcessor接口的postProcessBeforeInitialization、postProcessAfterInitialization,从而实现对bean的加强。具体的回调过程,在AbstractAutowireCapableBeanFactory的initializeBean办法中: protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }beanPostProcessor的注册Spring初始化时,refresh()办法中注册beanPostProcessor: ...

July 2, 2023 · 1 min · jiezi

关于java:Java基础语法八

包的介绍在java中,包是用来分门别类的治理各种不同程序的,相似于文件夹,建包有利于程序的治理和保护。 如果一个类是属于某个包,那么会类所在的文件上方增加package 包名的代码。 例如: 在本人程序中调用其余包下的程序须要留神: 如果以后程序中,要调用本人所在包下的其余程序,能够间接调用(同一个包下的类,相互能够间接调用)。如果以后程序中,要调用其余包下的程序,则必须在以后程序中导包,才能够拜访!导包格局:import 包名.类名。如果以后程序中,要调用java提供的程序,也须要先导包才能够应用;然而java.lang包下的程序是不须要咱们导包的,能够间接应用。如果以后程序中,要调用多个不同包下的程序,而这些程序名正好一样,此时默认只能导入一个程序,另一个程序必须带包名拜访。String对象介绍String对象在Java中属于一个援用类型数据,在java中提供了两种形式来创立一个String对象,别离是间接应用双引号创立以及应用实例化一个String类来创立。 例如: public static void main(String[] args) { // 创立字符串的两种模式 // 1. 应用双引号 String str1 = "这是一个字符串"; // 2. 应用类来创立String String str2 = new String("abc123"); System.out.println(str1); //这是一个字符串 System.out.println(str2); //abc123 }在创立字符串的时候,除了在new String的时候传递一个字符串以外,还能够传入一个字符数组,比方: char[] chars = { 'a', 'b', 'c' };String str = new String(chars);System.out.println(str); // abc// 也能够什么都不传递,创立一个空对象String str2 = new String();System.out.println(str2);String罕用办法

July 2, 2023 · 1 min · jiezi

关于java:Solon-也是-SSE-开发的后端优选最近因-chatGPT-火了-SSE

Solon 2.3.6 在开发异步接口时,顺带也为 Solon Web 提供了 SSE (Server-Sent Events) 协定的反对插件: <dependency> <groupId>org.noear</groupId> <artifactId>solon.web.sse</artifactId></dependency>如果不熟 SSE 的,能够通过搜索引擎查问理解下。//集体还是感觉 ws 更不便用:) 应用示例按群里用户的要求,体验形式尽量采纳大家相熟的命名与格调。 @Controllerpublic class SseDemoController { static Map<String, SseEmitter> emitterMap = new HashMap<>(); @Mapping("/sse/{id}") public SseEmitter sse(String id) { //3000L 是后端异步超时 return new SseEmitter(3000L) .onCompletion(() -> emitterMap.remove(id)) .onError(e -> e.printStackTrace()) .onInited(s -> emitterMap.put(id, s)); //在 onInited 里,能够发消息(初始化实现之前,是不能发消息的) } @Mapping("/sse/put/{id}") public String ssePut(String id) { SseEmitter emitter = emitterMap.get(id); if (emitter == null) { return "No user: " + id; } String msg = "test msg -> " + System.currentTimeMillis(); emitter.send(new SseEvent().id(Utils.guid()).data(msg).reconnectTime(1000L)); return "Ok"; } @Mapping("/sse/del/{id}") public String sseDel(String id) { SseEmitter emitter = emitterMap.get(id); if (emitter != null) { emitter.complete(); } return "Ok"; }}注意事项这个插件可能须要把线程数调大些#服务 http 最小线程数(默认:0示意主动,反对固定值 2 或 内核倍数 x2)server.http.coreThreads: 0 #服务 http 最大线程数(默认:0示意主动,反对固定值 32 或 内核倍数 x32)server.http.maxThreads: 0更多配置可参考:《利用罕用配置阐明》 ...

July 2, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-定制Bean

Customizing the Nature of a Bean,最早筹备跳过这部分内容,然而感觉这部分内容是Spring Bean生命周期中的一个重要局部,跳过了可能会影响通往NB之路,所以还是要认真学习一下。 Spring通过三种类型的接口实现对Bean行为或状态的扭转或定制: 生命周期回调ApplicationContextAware and BeanNameAware其余Aware接口生命周期回调有两种类型的回调:初始化回调、临终回调。对不起我不应该管这个DisposableBean接口的destroy()办法叫临终回调,不过的确比拟形象。 Spring提供了多种不同抉择来实现申明周期回调: 通过InitializingBean接口或DisposableBean接口实现通过配置init-method实现应用JSR-250 @PostConstruct和@PreDestroy注解通过BeanPostProcessor接口通过Lifecycle接口初始化回调实现InitializingBean的afterPropertiesSet()办法,afterPropertiesSet()办法在咱们上一篇文章剖析Bean创立过程的9步工作法的第6步中会被调用到: protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }从下面源码能够看到,除InitializingBean接口之外,还能够通过init-method的形式进行初始化,不过,极其状况下:两者同时应用,并且init-method也配置为afterPropertiesSet()办法,那么InitializingBean接口的afterPropertiesSet()办法会被回调、init-method办法不会。 ...

July 1, 2023 · 2 min · jiezi

关于java:SpringBoot-如何优雅的进行全局异常处理

在SpringBoot的开发中,为了进步程序运行的鲁棒性,咱们常常须要对各种程序异样进行解决,然而如果在每个出异样的中央进行独自解决的话,这会引入大量业务不相干的异样解决代码,减少了程序的耦合,同时将来想扭转异样的解决逻辑,也变得比拟艰难。这篇文章带大家理解一下如何优雅的进行全局异样解决。为了实现全局拦挡,这里应用到了Spring中提供的两个注解,@RestControllerAdvice和@ExceptionHandler,联合应用能够拦挡程序中产生的异样,并且依据不同的异样类型别离解决。上面我会先介绍如何利用这两个注解,优雅的实现全局异样的解决,接着解释这背地的原理。 1. 如何实现全局拦挡?1.1 自定义异样解决类在上面的例子中,咱们继承了ResponseEntityExceptionHandler并应用@RestControllerAdvice注解了这个类,接着联合@ExceptionHandler针对不同的异样类型,来定义不同的异样解决办法。这里能够看到我解决的异样是自定义异样,后续我会开展介绍。 ResponseEntityExceptionHandler中包装了各种SpringMVC在解决申请时可能抛出的异样的解决,处理结果都是封装成一个ResponseEntity对象。ResponseEntityExceptionHandler是一个抽象类,通常咱们须要定义一个用来解决异样的应用@RestControllerAdvice注解标注的异样解决类来继承自ResponseEntityExceptionHandler。ResponseEntityExceptionHandler中为每个异样的解决都独自定义了一个办法,如果默认的解决不能满足你的需要,则能够重写对某个异样的解决。@Log4j2 @RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { /** * 定义要捕捉的异样 能够多个 @ExceptionHandler({}) * * @param request request * @param e exception * @param response response * @return 响应后果 */ @ExceptionHandler(AuroraRuntimeException.class) public GenericResponse customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) { AuroraRuntimeException exception = (AuroraRuntimeException) e; if (exception.getCode() == ResponseCode.USER_INPUT_ERROR) { response.setStatus(HttpStatus.BAD_REQUEST.value()); } else if (exception.getCode() == ResponseCode.FORBIDDEN) { response.setStatus(HttpStatus.FORBIDDEN.value()); } else { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); } return new GenericResponse(exception.getCode(), null, exception.getMessage()); } @ExceptionHandler(NotLoginException.class) public GenericResponse tokenExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) { log.error("token exception", e); response.setStatus(HttpStatus.FORBIDDEN.value()); return new GenericResponse(ResponseCode.AUTHENTICATION_NEEDED); } }1.2 定义异样码这里定义了常见的几种异样码,次要用在抛出自定义异样时,对不同的情景进行辨别。 ...

July 1, 2023 · 3 min · jiezi

关于java:Java基础语法六

阐明本大节为Java根底语法练习环节,利用前几大节解说过的内容进行练习,达到坚固的目标。 买飞机票 public static void main(String[] args) { double price = calcDiscount(1000, 8, "经济舱"); System.out.println(price);}public static double calcDiscount(double price, int month, String type) { // 判断月份 if (month >= 5 && month <= 10) { // 淡季 switch(type) { case "头等舱": price *= 0.9; break; case "经济舱": price *= 0.85; break; } } else { // 旺季 switch (type) { case "头等舱": price *= 0.7; break; case "经济舱": price *= 0.65; break; } } return price;}开发验证码 ...

June 30, 2023 · 2 min · jiezi

关于java:Java-8中map和flatMap方法有什么区别

在Java 8中,map()和flatMap()是Stream API中的两个罕用办法,用于对流中的元素进行转换操作。它们的次要区别在于它们的返回类型和转换形式。 map()办法: map()办法将流中的每个元素都映射到另一个对象。它接管一个函数作为参数,该函数将以后流中的每个元素转换为另一个对象。map()办法的返回类型是一个新的Stream对象,其中蕴含了通过映射后的元素。map()办法保留了流的构造,也就是说,原始流中的每个元素都会依照映射函数的规定进行转换,并放入新的流中。示例代码: List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<Integer> nameLengths = names.stream() .map(name -> name.length()) .collect(Collectors.toList());System.out.println(nameLengths); // 输入:[5, 3, 7]flatMap()办法: flatMap()办法将流中的每个元素都转换为蕴含零个或多个元素的流。它接管一个函数作为参数,该函数将以后流中的每个元素转换为一个流。flatMap()办法的返回类型是一个新的Stream对象,其中蕴含了所有转换后的元素。这些元素被合并成一个新的流。flatMap()办法将每个元素转换为流后,会将所有的流合并为一个流,而不保留原始流的构造。示例代码: List<List<Integer>> numbers = Arrays.asList( Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9));List<Integer> allNumbers = numbers.stream() .flatMap(list -> list.stream()) .collect(Collectors.toList());System.out.println(allNumbers);// 输入:[1, 2, 3, 4, 5, 6, 7, 8, 9]总结一下,map()办法用于映射每个元素到另一个对象,而flatMap()办法用于将每个元素转换为一个流,并将所有转换后的流合并成一个流。

June 30, 2023 · 1 min · jiezi

关于java:如何在Java-8中使用Lambda表达式

首先,Lambda表达式是Java 8中引入的一种匿名函数语法。它容许咱们以更简洁和灵便的形式传递代码块作为参数或将其赋值给变量。Lambda表达式能够用于代替应用匿名类实现接口的传统形式。 老手从头看,有教训者间接看文章最初两个示例 Lambda表达式的语法如下: (parameters) -> { body } 其中,(parameters)指定了办法的参数列表,能够为空或蕴含一个或多个参数;->是Lambda运算符,用于将参数列表与表达式的主体分隔开;{ body }是办法的主体,能够是单个表达式或一系列语句。上面是一些常见的Lambda表达式的应用示例: 无参Lambda表达式: Runnable runnable = () -> System.out.println("Hello Lambda!");在这个示例中,咱们创立了一个Runnable接口的实例,应用Lambda表达式定义了其run()办法的实现。单个参数Lambda表达式: Consumer<String> consumer = (String message) -> System.out.println(message);consumer.accept("Hello Lambda!");在这个示例中,咱们创立了一个Consumer接口的实例,应用Lambda表达式定义了其accept()办法的实现。多参数Lambda表达式: Comparator<Integer> comparator = (Integer a, Integer b) -> a.compareTo(b);int result = comparator.compare(5, 10);在这个示例中,咱们创立了一个Comparator接口的实例,应用Lambda表达式定义了其compare()办法的实现。Lambda表达式块: Supplier<String> supplier = () -> { String message = "Hello"; return message + " Lambda!";};String result = supplier.get();在这个示例中,咱们创立了一个Supplier接口的实例,应用Lambda表达式块定义了其get()办法的实现。Lambda表达式的劣势在于简化代码、进步可读性和灵活性。它让咱们可能更专一地编写外围逻辑,而不用过多关注简单的语法和匿名类的实现。 在Java中,函数式接口(Functional Interface)是指只蕴含一个形象办法的接口。函数式接口是Lambda表达式的根底,在Java 8之后引入,用于反对函数式编程的个性。 以下是函数式接口的特点: 只蕴含一个形象办法:函数式接口只能有一个未实现的形象办法。这个形象办法定义了Lambda表达式要执行的行为。 能够有默认办法和静态方法:函数式接口能够蕴含默认办法(default method)和静态方法(static method)。默认办法提供了接口的默认实现,静态方法提供了与接口相关联的工具办法。 能够通过 @FunctionalInterface 注解标记:尽管不是强制要求,但倡议应用 @FunctionalInterface 注解来明确标记一个接口是函数式接口。这样做能够确保接口只蕴含一个形象办法,同时还能让编译器进行额定的查看。 ...

June 30, 2023 · 3 min · jiezi

关于java:狂收-32k-star百度开源压测工具可模拟几十亿的并发场景太强悍了

dperf 是一款基于 DPDK 的 100Gbps 网络性能和负载测试软件,可能每秒建设千万级的 HTTP 连贯、亿级别的并发申请和数百 Gbps 的吞吐量。 长处性能弱小:基于 DPDK,应用一台一般 x86 服务器就能够产生微小的流量:千万级的 HTTP 每秒新建连接数,数百 Gbps 的带宽,几十亿的并发连接数统计信息具体:可能输入具体的统计信息,并且辨认每一个丢包应用场景丰盛:可用于对四层负载平衡等四层网关进行性能压力测试、长稳测试可用于对云上虚拟机的网络性能进行测试可用于对网卡性能、CPU 的网络报文解决能力进行测试压测场景下,可作为高性能的 HTTP Server 或 HTTP Client 独自应用性能HTTP 每秒新建连接数Client CoresServer CoresHTTP CPS112,101,044224,000,423447,010,7436610,027,172HTTP 吞吐Client CoresServer CoresRX(Gbps)TX(Gbps)Client CPU Usage(%)Server CPU Usage(%)111818605922353560594446464343HTTP 并发连接数Client CoresServer CoresCurrent ConnectionsClient CPU Usage(%)Server CPU Usage(%)11100,000,000343922200,000,000363944400,000,0004041UDP TX PPSClient CoresTX MPPSClient CPU Usage(%)115.9695229.9595434.9267635.9254837.1222测试环境配置dperf 的以上性能数据,基于上面的配置测试失去: 内存: 512GB(大页 100GB)网卡: Mellanox MT27710 25Gbps * 2内核: 4.19.90统计数据dperf 每秒输入多种统计数据: TPS, CPS, 各种维度的 PPSTCP/Socket/HTTP 级别的谬误数丢包数依照 TCP Flag 分类的报文重传数seconds 22 cpuUsage 52 pktRx 3,001,058 pktTx 3,001,025 bitsRx 2,272,799,040 bitsTx 1,920,657,600 dropTx 0 arpRx 0 arpTx 0 icmpRx 0 icmpTx 0 otherRx 0 badRx 0 synRx 1,000,345 synTx 1,000,330 finRx 1,000,350 finTx 1,000,350 rstRx 0 rstTx 0 synRt 0 finRt 0 ackRt 0 pushRt 0 tcpDrop 0 skOpen 1,000,330 skClose 1,000,363 skCon 230 skErr 0 httpGet 1,000,345 http2XX 1,000,350 httpErr 0 ierrors 0 oerrors 0 imissed 0 开始应用设置大页#参考如下参数编辑 '/boot/grub2/grub.cfg',而后重启OS linux16 /vmlinuz-... nopku transparent_hugepage=never default_hugepagesz=1G hugepagesz=1G hugepages=8 编译 DPDK#编辑'config/common_base'关上PMD开关 #Mellanox CX4/CX5 requires 'CONFIG_RTE_LIBRTE_MLX5_PMD=y' #HNS3 requires 'CONFIG_RTE_LIBRTE_HNS3_PMD=y' #VMXNET3 requires 'CONFIG_RTE_LIBRTE_VMXNET3_PMD=y' TARGET=x86_64-native-linuxapp-gcc #or arm64-armv8a-linuxapp-gcc cd /root/dpdk/dpdk-stable-19.11.10 make install T=$TARGET -j16 编译 dperfcd dperf make -j8 RTE_SDK=/root/dpdk/dpdk-stable-19.11.10 RTE_TARGET=$TARGET 绑定网卡#Mellanox网卡跳过此步 #假如PCI号是0000:1b:00.0 modprobe uio modprobe uio_pci_generic /root/dpdk/dpdk-stable-19.11.10/usertools/dpdk-devbind.py -b uio_pci_generic 0000:1b:00.0 启动 dperf server#dperf server监听6.6.241.27:80, 网关是6.6.241.1 ./build/dperf -c test/http/server-cps.conf 从客户端发送申请#客户端IP必须要在配置文件的'client'范畴内 ping 6.6.241.27 curl http://6.6.241.27/ 运行测试上面的例子运行一个 HTTP CPS 压力测试。在 server 端运行 dperf ./build/dperf -c test/http/server-cps.conf ...

June 30, 2023 · 2 min · jiezi

关于java:Java-8中filtermapsorteddistinctlimitreduce的用法

在Java 8中,引入了一个名为Stream的全新API,用于解决汇合数据。Stream API提供了一种流式解决数据的办法,能够通过链式调用一系列的操作来解决汇合中的元素。以下是次要的Stream API及其代码示例: 1. filter():依据指定条件过滤流中的元素,并返回一个新的流。 java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 过滤出偶数 List<Integer> evenNumbers = numbers.stream() .filter(number -> number % 2 == 0) .collect(Collectors.toList()); System.out.println(evenNumbers); // 输入: [2, 4] }}2. map():将流中的元素映射为另一种类型,并返回一个新的流。 java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class Main { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 将名字转换为大写 List<String> upperCaseNames = names.stream() .map(name -> name.toUpperCase()) .collect(Collectors.toList()); System.out.println(upperCaseNames); // 输入: [ALICE, BOB, CHARLIE] }}3. sorted():对流中的元素进行排序,默认是升序。也能够传入自定义的Comparator来进行排序。 ...

June 30, 2023 · 2 min · jiezi

关于java:Java基础语法五

办法办法是一种语法结构,它能够把一段代码封装成一个性能,以便反复调用。 办法定义的语法格局 例如, public static void main(String[] args) { sayHello(); // 调用add办法 int val = add(10, 20); System.out.println(val);//30}// 定义一个sayHello办法,没有参数也没有返回值public static void sayHello() { // 没有返回值的状况下须要设置void System.out.println("hello,world!");}// 定义一个add办法,该办法返回值为int,num1和num2两个参数为intpublic static int add(int num1, int num2) { return num1 + num2;}应用办法的益处 进步了代码的复用性,进步了开发效率。让程序的逻辑更清晰。定义方法的时候,须要留神: 办法在类中的地位不重要,然而不能定义在另一个办法外面。return 前面不能编写代码,属于有效代码。无返回值的时候,须要设置void。在办法中不能设置return。练习 1、 计算1-n的和。 public static void main(String[] args) { int val = getSum(100); System.out.println(val); // 4950}// 计算1-n的和public static int getSum(int n) { int sum = 0; for(int i = 0; i < n; i++) { sum += i; } return sum;}2、判断一个数是奇数还是偶数。 ...

June 30, 2023 · 1 min · jiezi

关于java:Java基础语法四

数组能够把数组了解为是一个容器,用来存储一批同种类型的数据。 定义形式如下: // 定义一个数组int[] nums = {10, 20, 30, 40};String[] names = {"张三", "李四"};一般来说,开发者会用数组存储一系列相干的值。 动态初始化数组 示例: // 动态初始化数组-残缺写法String[] names = new String[]{"zs", "root", "ls"};int[] nums = new int[]{10,20,30};double[] scores = new double[]{55.9, 99.5, 60.0};boolean[] status = new boolean[]{true, false};// 简化写法int[] ages = {10,20,30};String[] list = {"zs", "ls"};留神: "数据类型[] 数组名" 也能够写成"数据类型 数组名[]"。这种写法在java中并不举荐。什么类型的数组只能寄存什么类型的数据。数组在内存中的基本原理 当在代码中间接打印一个数组的时候,就会失去数组在内存中的地址,例如: int[] nums = {10, 20, 30};System.out.println(nums); // [I@10f87f48打印出的[I@10f87f48后果是java对数组内存地址的一个形容,[示意是一个数组,I示意int类型,@示意这个数组在哪个地址,10f89f48就是具体的地址,应用十六进制示意。 留神:数组变量名中存储的是数组在内存中的地址,数组是一种援用数据类型。数组元素拜访 例如: int[] nums = {10, 20, 30};// 读取数组中的值System.out.println(nums[0]); // 10// 批改数组中的值nums[0] = 20;System.out.println(nums[0]); // 20// 获取数组的长度System.out.println(nums.length); // 3 // 获取数组的最大索引System.out.println(nums[nums.length - 1]); // 30遍历数组能够通过for循环来遍历数组: ...

June 29, 2023 · 2 min · jiezi

关于java:Redis和它的键们4ZSet

后面讲了这么多数据结构,然而仍不如我讲述Zset如此让人冲动: 它是set,然而竟然有序在有序的状况下,单点查找还能放弃O(1)单点查找O(1)的状况下,范畴查找还能放弃O(logN)为什么会有这样完满的数据结构呢?在这样简单的内部结构中,它要如何保障它的主线程不被阻塞呢? Zset的渐进式编码 如果有序汇合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会应用压缩列表作为 Zset 类型的底层数据结构;如果有序汇合的元素不满足下面的条件,Redis 会应用跳表作为 Zset 类型的底层数据结构;4.1 Zset的数据结构 压缩列表咱们就不说了,当数据规模较小时,各种数据结构的速度差别并不大,此时咱们首要的工作时升高其占用空间,这是压缩表存在的首要意义。 咱们往往能够晓得,索引的概念是工夫换空间的要害,Zset能如此迅速的要害也就在于它在此方面做了肯定的取舍,然而保护索引实际上是一个比拟费劲的操作,尤其是保护那些和有序相干的索引,比方MySQL的页决裂,这往往对于引擎是比拟大的累赘,如何在建设规模宏大的索引同时不阻塞主线程?是咱们明天在扫视Zset的同时须要抱着的问题。 Zset的罕用接口//返回有序汇合key中元素member的分值//复杂度:O(1)ZSCORE key member//正序获取有序汇合key从start下标到stop下标的元素//复杂度:O(logN)ZRANGE key start stop [WITHSCORES]//倒序获取有序汇合key从start下标到stop下标的元素//复杂度:O(logN)ZREVRANGE key start stop [WITHSCORES]//返回指定成员区间内的成员,按字典正序排列, 分数必须雷同。//复杂度:O(m) m为与key元素分数雷同的元素数目ZRANGEBYLEX key min max [LIMIT offset count]看到这里,置信咱们大家对于Zset的构造曾经跃然纸上,其实,类比来看,Zset的构造就是对value建设索引的LinkedHashMap。 这个索引的模式是怎么样的呢?会像MySQL那样是多层树吗?显然不是,MySQL建设一个那样的树是有着微小的保护老本的,页决裂和页合并是一种微小的开销,因而B+树对于频繁增删的零碎并不敌对,而对于Redis更是灭顶之灾,如果主线程要在这样一种构造中耗费这么多实现,那么业务需要马上就会被阻塞,高并发也就无从谈起了,这是Redis相对不容许的状况,咱们来看一下,Redis是如何奇妙地解决这个问题的。 4.1.1 skipList无论是何种索引,如果要反对范畴查找,那就逃脱不了排序+多级查找的状况,跳表也不例外,咱们先来察看在Zset中,各个节点如何组织 上图中咱们能够看到三个元素”banana” “cherry” “apple” 他们各自的分数是5.0 6.0 8.5,并且曾经依照分数大小进行了排序 这种构造是如何实现查问的?一板一眼的讲可能不太好明确,咱们用一个比喻来说,每一个如上所示的节点,都比作一棵高大的灌木或者树木,它的高度就是层高,现在咱们须要查找此Zset,获取到header之后,从上往下遍历,相当于在一座高楼上从上往下行走,如果极目远眺,没有任何树木的话,它就会往下走一层,直到他下到了L2…. 对于两个长得非常高大地灌木,因为指向的是它的整个节点的指针,咱们能够间接获取它的score,和本人须要查问的score比对后,比本人的大,那么将会间接跳到下一个领有26层高度的灌木上进行查问,如果下一颗灌木上的比要查问的大,那么将会把层高改成25层,并且通过BW指针往回查找,咱们权且假如,咱们须要寻找的是分数为51的树,那么咱们最终会通过如上的遍历形式,最终在第二颗高度为L24的灌木上寻找到它 而如果咱们要寻找的是51.5,那么最终,查问者会在两头两颗灌木上横跳,直到跳到L1层,发现无处可跳时,查问将会终止,返回没有此后果 这些灌木索引,是如何保护的?咱们查问者所在的察看楼,理论就是一组链表数组,每一个元素都是一个链表,index为24的链表,实际上就是层高为25的所有节点,依照score从小到大组成的指针链表 当咱们向跳表退出一个高度为5的灌木时,咱们首先要做的,就是通过score找到它在链表中的该有的地位,这个环节须要O(logN)的开销,在那之后,就是扭转各个层数的与本人的关系,这个过程咱们能够了解为,在每一层往左往右察看(往左:通过BW向前寻找存在我这个层高的灌木;往右:通过L1指针向后寻找存在我这个层高的灌木),每层察看到的两株灌木都须要扭转互相的索引关系,因为本人有5层,故最多须要扭转5个前驱灌木对本人的指针,以及本人对至少5个后继灌木的指针,这个环节往往是最大的隐患,因为遍历的缘故开销达到O(N)也是时有发生的为何要采纳概率的形式管制这些灌木的高度?在索引中采纳概率是一个新鲜事,然而咱们也能够想想如果不必这种自增长,而采取严格的二分式索引,保护老本是怎么的,采纳BST+节点保留地址的形式来构建索引仿佛是个可行的计划,然而二叉搜寻树是须要通过旋转来保持平衡的,所以维持均衡的开销也是咱们难以承受的 对于BST+节点保留地址的尝试通过BST查找到须要插入的地位,开销O(logN)为了维持严格的均衡,咱们还须要把此score插入到一个不会使左右子树高度差大于1的子树上这样的构想是跳表的完满实现,就像是非聚簇索引一样的优雅,然而它的缺点往往在不起眼处: 难道地址的寻址就不须要工夫吗?这跟内存的时序无关,然而也是很难疏忽的常数开销,如果score和member是聚簇在一起的,寻址的工夫将会十分短暂。当然我不是说此道破灭了,然而远没有它看起来这么美妙,然而,如果采纳聚簇式的BST,它依然是在空间和性能上都不占优势的抉择 从内存占用上来比拟,跳表比非聚簇均衡树更灵便一些。均衡树每个节点蕴含 2 个指针(别离指向左右子树),而跳表每个节点蕴含的指针数目均匀为 1/(1-p),具体取决于参数 p 的大小。如果像 Redis里的实现一样,取 p=1/4,那么均匀每个节点蕴含 1.33 个指针,比均衡树更有劣势。在做范畴查找的时候,跳表比非聚簇均衡树操作要简略。在均衡树上,咱们找到指定范畴的小值之后,还须要以中序遍历的程序持续寻找其它不超过大值的节点。如果不对均衡树进行肯定的革新,这里的中序遍历并不容易实现。而在跳表上进行范畴查找就非常简单,只须要在找到小值之后,对第 1 层链表进行若干步的遍历就能够实现。从算法实现难度上来比拟,跳表比非聚簇均衡树要简略得多。均衡树的插入和删除操作可能引发子树的调整,逻辑简单,而跳表的插入和删除只须要批改相邻节点的指针,操作简略又疾速。4.1.2 Zset总结说到这里,Zset的构造咱们的大抵就介绍完了,咱们来总结一下: 在数据规模不大时,Zset为了缩小空间占用,采纳的是压缩列表的存储形式,毕竟繁多的索引空间占用不菲,但效率却并没比遍历查问进步多少,但要留神,此时Zset依然是排序的,每次咱们退出Member都会依照其Score排序插入随着数据规模增长,Zset会改用skipList为根底的的形式存储元素,skipList是一种带索引的双向链表构造,它采纳了奇妙的形式保护了索引的构造,从而极大水平升高了它的复杂程度,这肯定水平上遵循了redis非阻塞式设计的初衷为了迅速的定位Member,Zset引入了map来定位每一个Node<Member,Score>,这样咱们能够实现O(1)的复杂度以查问到Member的分数至此Zset便没有什么神秘的了,跳表+字典是它的底层实现,随机式的索引构建形式是它防止阻塞的秘诀。Redis的单线程设计是它被认为简略的起因,但它的种种设计又在通知咱们它为了维持单线程的鲁棒性做出了远比它外表看起来的简单的致力,这些设计或通过实际考验或通过数学证实,都给Redis留下了不可磨灭的烙印。 4.2 Zset In Action4.2.1 排行榜Zset比拟典型的应用场景就是排行榜。例如学生问题的排名榜、游戏积分排行榜、视频播放排名、电商零碎中商品的销量排名等。 ...

June 29, 2023 · 1 min · jiezi

关于java:Java基础语法三

流程管制if分支的三种模式 例如: Scanner sc = new Scanner(System.in);System.out.println("请输出你的温度");double temperature = sc.nextDouble();if (temperature > 38.0) { System.out.println("您可能要嘎了");} else if(temperature > 37.0) { System.out.println("您可能须要住院了");} else if (temperature > 36.0){ System.out.println("您可能须要吃药");} else { System.out.println("啥事没有,玩去吧");}switch语法格局 例如: Scanner sc = new Scanner(System.in);System.out.println("请通知我明天是周几?");String day = sc.next();switch (day) { case "周一": System.out.println("埋头苦干"); break; case "周二": System.out.println("申请帮忙"); break; case "周三": System.out.println("啤酒小龙虾"); break; case "周四": System.out.println("帮忙女同学"); break; case "周五": System.out.println("大吉大利,今晚吃鸡"); break; case "周六": System.out.println("相亲"); break; case "周日": System.out.println("郁郁寡欢,筹备下班"); break; default: System.out.println("大哥,你连周几都能搞错...");} ...

June 29, 2023 · 1 min · jiezi

关于java:Integer与int详解

说说上面代码的输入:Integer a1 = 10, b1 = 128;Integer a2 = 10, b2 = 128;Integer a3 = new Integer(10);Integer b3 = new Integer(128);System.out.println(a1==a2);//trueSystem.out.println(a1==a3);//falseSystem.out.println(b1==b2);//falseSystem.out.println(b1==b3);//false上述代码详解Integer为援用对象,那么两个Integer对象应用"=="比拟的就是空间地址,如果a1==a2那就阐明a1与a2的空间地址是雷同的,为什么会雷同呢?b1与b2的空间地址又为什么不雷同了呢?实际上对于第一与第二行代码,java在编译期做了优化,实际上执行的代码如下 Integer a1 = = Integer.valueOf(10);那么为什么应用valueOf办法创立Integer对象时a1与a2的空间地址是雷同的而b1与b2的空间地址又不同了呢?下图为valueOf办法的源码从图中能够看到对不同的值会采纳不同的创立形式,这所有仿佛都与IntegerCache这个对象无关,cache为缓存的意思,不难猜出如果合乎if中的条件则从缓存中获取一个对象,不合乎则会new一个对象.上面进入到类IntegerCache中看看.正文为起初增加 private static class IntegerCache {//IntegerCache.low的值为-128,被final润饰示意必须赋值且赋值后便不可再次更改 static final int low = -128; //high值虽也被final润饰但并未赋初值,意味着要在前面赋初值 static final int high; //用来存储缓存的Integer对象 static final Integer cache[]; static { int h = 127;//从虚拟机配置中获取配置"java.lang.Integer.IntegerCache.high"的值,能够本人批改虚拟机配置,默认没有该条配置 String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try {//将获取的String配置值转为int int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127);//配置的值若超过int类型能示意的最大值,必定是不行的,同时也是因为缓存的对象都是存在了数组中而数组的大小也是有上线的,超过数组最大下限就缓存不下了 h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); assert IntegerCache.high >= 127; } private IntegerCache() {}}综上能够看出在不批改虚拟机配置的状况下,Integer会在第一次调用valueOf办法时将-128与127的Integer对象进行缓存.所有应用valueOf获取的Integer对象若在范畴-128~127则都为缓存中的同一对象,而不在范畴内的值则会从新new一个Integer对象.回过头再看看开始的代码输入,是否跟料想的后果统一了呢 ...

June 29, 2023 · 1 min · jiezi

关于java:设计模式-创建型

创立型模式次要解决对象的创立问题,封装简单的创立过程,解耦对象的创立代码和应用代码。 罕用的:单例模式、工厂模式(工厂办法和形象工厂)、建造者模式 不罕用的:原型模式 一、单例模式一个类只容许创立一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式。 3个要点某个类只能有一个实例它必须自行创立这个实例它必须自行向整个零碎提供这个实例实现形式饿汉式当类被加载时,动态变量instance会被初始化,此时类的公有构造函数会被调用,单例类的惟一实例将被创立。如果应用饿汉式单例来实现ID生成器IdGenerator类的设计,则不会呈现创立多个单例对象的状况,可确保单例对象的唯一性。 public class IdGenerator { private AtomicLong id = new AtomicLong(0);private static final IdGenerator instance = new IdGenerator();private IdGenerator() {}public static IdGenerator getInstance() {  return instance;}public long getId() {   return id.incrementAndGet();}}懒汉式懒汉式单例在第一次调用getInstance()办法时实例化,在类加载时并不自行实例化,这种技术又称为提早加载(Lazy Load)技术,即须要的时候再加载实例。为了防止多个线程同时调用getInstance()办法,能够应用关键字synchronized。 该懒汉式单例类在getInstance()办法后面减少了关键字synchronized进行线程锁定,以解决多个线程同时拜访的问题。上述代码尽管解决了线程平安问题,然而每次调用getInstance()时都须要进行线程锁定判断,在多线程高并发拜访环境中,将会导致系统性能大大降低。 public class IdGenerator { private AtomicLong id = new AtomicLong(0);private static IdGenerator instance;private IdGenerator() {}public static synchronized IdGenerator getInstance() {  if (instance == null) {    instance = new IdGenerator();  }  return instance;}public long getId() {   return id.incrementAndGet();}}双重检测须要两次判空的起因:如果某一瞬间线程A和线程B都在调用getInstance()办法,此时instance对象为null值,均能通过“instance==null”的判断。因为实现了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例创立代码,线程B处于排队期待状态,必须期待线程A执行结束后才能够进入synchronized锁定代码。但当A执行结束时,线程B并不知道实例曾经创立,将持续创立新的实例,导致产生多个单例对象,违反单例模式的设计思维。因而须要进行进一步改良,在synchronized锁定代码中再进行一次“instance==null”判断,这种形式称为双重查看锁定(Double-Check Locking) ...

June 29, 2023 · 3 min · jiezi

关于java:后端面经Java公平锁和加锁流程

1. 偏心锁和非偏心锁1.1 基本概念偏心锁:线程依照到来的先后顺序,排队期待应用资源。非偏心锁:线程不肯定依照先后顺序应用资源,而是可能呈现“插队”的状况。拿游乐场期待娱乐我的项目举例,一般游客只能依照先后顺序排队期待应用游乐设施,这就是偏心锁,然而一般入口加上优速通,显然VIP游客能够快人一步,这就有点非偏心锁的意思了。 1.2 ReentrantLock 的偏心锁和非偏心锁在《【后端面经-Java】Synchronize和ReentrantLock区别》这篇博客中,咱们比照过synchronized和ReentrantLock的区别,其中synchronized是一种非偏心锁,而ReentrantLock默认是非偏心锁,然而也可设置为偏心锁。具体设置形式如下: //生成一个偏心锁static Lock lock = new ReentrantLock(true);//生成一个非偏心锁static Lock lock = new ReentrantLock(false);static Lock lock = new ReentrantLock();//默认参数就是false,这种写法也可通过更改构造函数中的参数,咱们能够批改ReentrantLock的锁类型,true示意偏心锁,false示意非偏心锁。构造函数具体代码如下所示: public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();//FairSync示意偏心锁,NonfairSync示意非偏心锁}2. 加锁流程2.1 ReentrantLock 和 AQS 的关系在【后端面经-Java】AQS详解这篇博客中,咱们具体解说了AQS的原理,其中提到了 AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如罕用的ReentrantLock。可就是说,ReentrantLock也是通过AQS来实现的,而自定义同步锁须要实现AQS框架中的tryAcquire()和tryRelease()办法或者tryAcquireShared()和tryReleaseShared()办法。 因而,ReentrantLock的加锁流程咱们可用查看tryAcquire()办法理解。 2.2 偏心锁-加锁流程偏心锁的tryAcquire()办法源码如下所示: protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0,acquires)) {//这里判断了队列中是不是还有其余线程在期待 && 以后资源是否可用? //间接获取资源 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) {//如果有其余线程在期待或者资源不可用,线程进入期待态,排队期待 int nextc = c + acquires; if (nextc < 0) { throw new Error("Maximum lock count exceeded"); } setState(nextc); return true; } return false;}代码流程如下所示: ...

June 29, 2023 · 1 min · jiezi

关于java:Java-8引入的Stream-API附代码示例

在Java 8中,引入了Stream API,它是一种用于解决汇合数据的新形式。Stream API提供了一种函数式编程的办法来解决汇合,使得代码更加简洁和易读。上面是一些罕用的Stream API及其代码示例: filter():依据指定条件过滤流中的元素,并返回一个新的流。 import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 过滤出偶数 List<Integer> evenNumbers = numbers.stream() .filter(number -> number % 2 == 0) .collect(Collectors.toList()); System.out.println(evenNumbers); }}// 输入: [2, 4]map():将流中的元素映射为另一种类型,并返回一个新的流。 import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class Main { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 将名字转换为大写 List<String> upperCaseNames = names.stream() .map(name -> name.toUpperCase()) .collect(Collectors.toList()); System.out.println(upperCaseNames); }}// 输入: [ALICE, BOB, CHARLIE]flatMap():将流中的每个元素映射为一个流,而后将这些流扁平化为一个流。 ...

June 29, 2023 · 4 min · jiezi

关于java:蔚来真题和答案主打一个简单

问题起源如下: <img src="https://cdn.nlark.com/yuque/0/2023/png/92791/1687946200989-e5852be8-69f0-49a9-9b40-f3960db36c2d.png" width="50%"> 问题链接:https://www.nowcoder.com/discuss/493178141461041152答案解析1.解释脏读/不可反复读/幻读脏读:指一个事务读取到了另一个事务为提交保留的数据,之后此事务进行了回滚操作,从而导致第一个事务读取了一个不存在的脏数据。不可反复读:在同一个事务中,同一个查问在不同的工夫失去了不同的后果。例如事务在 T1 读取到了某一行数据,在 T2 工夫从新读取这一行时候,这一行的数据曾经产生批改,所以再次读取时失去了一个和 T1 查问时不同的后果。幻读:同一个查问在不同工夫失去了不同的后果,这就是事务中的幻读问题。例如,一个 SELECT 被执行了两次,然而第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。 不可反复读和幻读的区别不可反复读的重点是批改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为两头有其余事务提交了批改);幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为两头有其余事务提交了插入/删除)。 2.索引生效的场景有哪些?常见的索引生效场景有以下这些: 未遵循最左匹配准则应用列运算应用函数办法类型转换应用 is not null谬误的含糊匹配,应用右 % 开始查问。具体内容请参考:https://www.javacn.site/interview/mysql/indexinvalid.html 3.Explain执行打算用过吗?Explain 是用来剖析 SQL 的执行状况的,explain 应用如下,只须要在查问的 SQL 后面增加上 explain 关键字即可,如下图所示:而以上查问后果的列中,咱们最次要察看 key 这一列,key 这一列示意理论应用的索引,如果为 NULL 则示意未应用索引,反之则应用了索引。 以上所有后果列阐明如下: id — 抉择标识符,id 越大优先级越高,越先被执行;select_type — 示意查问的类型;table — 输入后果集的表;partitions — 匹配的分区;type — 示意表的查问类型;possible_keys — 示意查问时,可能应用的索引;key — 示意理论应用的索引;key_len — 索引字段的长度;ref— 列与索引的比拟;rows — 大略估算的行数;filtered — 按表条件过滤的行百分比;Extra — 执行状况的形容和阐明。4.Type字段有什么信息?Explain 执行打算中最重要的就是 type 字段,type 蕴含的信息如下: all — 扫描全表数据;index — 遍历索引;range — 索引范畴查找;index_subquery — 在子查问中应用 ref;unique_subquery — 在子查问中应用 eq_ref;ref_or_null — 对 null 进行索引的优化的 ref;fulltext — 应用全文索引;ref — 应用非惟一索引查找数据;eq_ref — 在 join 查问中应用主键或惟一索引关联;const — 将一个主键搁置到 where 前面作为条件查问, MySQL 优化器就能把这次查问优化转化为一个常量,如何转化以及何时转化,这个取决于优化器,这个比 eq_ref 效率高一点。5.binlog和redolog区别?binlog(二进制日志)和 redolog(重做日志)都是 MySQL 中的重要日志,但二者存在以下不同。 ...

June 29, 2023 · 3 min · jiezi

关于java:Java反射源码学习之旅-京东云技术团队

1 背景前段时间组内针对“拷贝实例属性是应该用BeanUtils.copyProperties()还是MapStruct”这个问题进行了一次强烈的battle。反对MapStruct的同学给出了他厌弃BeanUtils的理由:因为用了反射,所以慢。 这个理由一下子拉回了我边远的记忆,在我刚开始理解反射这个Java个性的时候,简直看到的每一篇文章都会有“Java反射不能频繁应用”、“反射影响性能”之类的话语,过后只是当一个论断记下了这些话,却没有深究过为什么,所以正好借此机会来探索一下Java反射的代码。 2 反射包构造梳理反射相干的代码次要在jdk rt.jar下的java.lang.reflect包下,还有一些相干类在其余包门路下,这里先按下不表。依照继承和实现的关系先简略划分下java.lang.reflect包: ① Constructor、Method、Field三个类型别离能够形容实例的构造方法、一般办法和字段。三种类型都间接或间接继承了AccessibleObject这个类型,此类型里次要定义两种办法,一种是通用的、对拜访权限进行解决的办法,第二种是可供继承重写的、与注解相干的办法。 ② 只看选中的五种类型,咱们平时所用到的一般类型,譬如Integer、String,又或者是咱们自定义的类型,都能够用Class类型的实例来示意。Java引入泛型之后,在JDK1.5中裁减了其余四种类型,用于泛型的示意。别离是ParameterizedType(参数化类型)、WildcardType(通配符类型)、TypeVariable(类型变量)、GenericArrayType(泛型数组)。 ③ 与②中形容的五种根本类型对应,下图这五个接口/类别离用来示意五种根本类型的注解相干数据。 ④ 下图为实现动静代理的相干类与接口。java.lang.reflect.Proxy次要是利用反射的一些办法获取代理类的类对象,获取其构造方法,由此结构出一个实例。 java.lang.reflect.InvocationHandler是代理类须要实现的接口,由代理类实现接口内的invoke办法,此办法会负责代理流程和被代理流程的执行程序组织。 3 指标类实例的结构源码以String类的对象实例化为例,看一下反射是如何进行对象实例化的。 Class<?> clz = Class.forName("java.lang.String");String s =(String)clz.newInstance();Class对象的结构由native办法实现,以java.lang.String类为例,先看看结构好的Class对象都有哪些属性: 能够看到目前只有name一个属性有值,其余属性临时都是null或者默认值的状态。 下图是 clz.newInstance() 办法逻辑的流程图,接下来对其中次要的两个办法进行阐明: 从上图能够看出整个流程有两个外围局部。因为通常状况下,对象的结构都须要依附类里的构造方法来实现,所以第一局部就是拿到指标类对应的Constructor对象;第二局部就是利用Constructor对象,结构指标类的实例。 3.1 获取Constructor对象首先上一张Constructor对象的属性图: java.lang.Class#getConstructor0 此办法中次要做的工作是首先拿到指标类的Constructor实例数组(次要由native办法实现),数组里每一个对象都代表了指标类的一个构造方法。而后对数组进行遍历,依据办法入参提供的parameterTypes,找到合乎的Constructor对象,而后从新发明一个Constructor对象,属性值与原Constructor统一(称为正本Constructor),并且正本Constructor的属性 root 指向源Constructor,相当于对源Constructor对象进行了一层封装。 因为在getConstructor0()办法将返回值返回给调用方之后,调用方在后续的流程里进行了constructor.setAccesssible(true)的操作,这个办法的作用是敞开对constructor这个对象拜访时的Java语言拜访查看。语言拜访查看是个耗时的操作,所以正当猜想是为了进步反射性能敞开了这个查看,又出于平安思考,所以将最原始的对象进行了封装。 private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) throws NoSuchMethodException{//1、拿到Constructor实例数组并进行筛选 Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); //2、通过对入参的比拟筛选出符合条件的Constructor for (Constructor<T> constructor : constructors) { if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) {//3、创立正本Constructor return getReflectionFactory().copyConstructor(constructor); } } throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));}3.2 指标类实例的结构sun.reflect.ConstructorAccessor#newInstance ...

June 29, 2023 · 2 min · jiezi

关于java:Spring-Boot-项目设计业务操作日志功能写得太好了

前言很久以前都想写这篇文章,始终没有空,但直到现在我对过后的情景还有印象,之所以有印象是因为需要很简略,业务操作日志的记录与查问的性能,然而具体实现真的很烂,具体的烂法会在背面示例里细说,领导以及客户层面很认可,一系列迷之操作,让我印象粗浅。 需要形容与剖析客户侧提出需要很简略:要对几个要害的业务性能进行操作日志记录,即什么人在什么工夫操作了哪个性能,操作前的数据报文是什么、操作后的数据报文是什么,必要的时候能够一键回退。 日志在业务零碎中是必不可少的一个性能,常见的有系统日志、操作日志等: 系统日志这里的系统日志是指的是程序执行过程中的关键步骤,依据理论场景输入的debug、info、warn、error等不同级别的程序执行记录信息,这些个别是给程序员或运维看的,个别在出现异常问题的时候,能够通过系统日志中记录的要害参数信息和异样提醒,疾速排除故障。 操作日志操作日志,是用户理论业务操作行为的记录,这些信息个别存储在数据库里,如什么工夫哪个用户点了某个菜单、批改了哪个配置等这类业务操作行为,这些日志信息是给普通用户或系统管理员看到。 通过对需要的剖析,客户想要是一个业务操作日志治理的性能: 1、记录用户的业务操作行为,记录的字段有:操作人、操作工夫、操作性能、日志类型、操作内容形容、操作内容报文、操作前内容报文 2、提供一个可视化的页面,能够查问用户的业务操作行为,对重要操作回溯; 3、提供肯定的治理性能,必要的时候能够对用户的误操作回滚; 背面实现 明确需要后,就是怎么实现的问题了,这里先上一个背面的实现案例,也是因为这一个背面案例,才让我对这个简略的需要印象粗浅。 这里我以一个人员治理的性能为例还原一下,过后的具体实现: 1、每个接口里都加一段记录业务操作日志的记录; 2、每个接口里都要捕捉一下异样,记录异样业务操作日志; 上面是伪代码: @RestController@Slf4j@BusLog(name = "人员治理")@RequestMapping("/person")public class PersonController2 { @Autowired private IPersonService personService; @Autowired private IBusLogService busLogService; //增加人员信息 @PostMapping public Person add(@RequestBody Person person) { try{ //增加信息信息 Person result = this.personService.registe(person); //保留业务日志 this.saveLog(person); log.info("//减少person执行实现"); }catch(Exception e){ //保留异样操作日志 this.saveExceptionLog(e); } return result; }}这种通过硬编码实现的业务操作日志治理性能,最大的问题就是业务操作日志收集与业务逻辑耦合重大,和代码反复,新开发的接口在实现业务逻辑后要织入一段业务操作日志保留的逻辑,已开发上线的接口,还要从新再批改织入业务操作日志保留的逻辑并测试,且每个接口须要织入的业务操作日志保留的逻辑是一样的。 举荐一个开源收费的 Spring Boot 实战我的项目: https://github.com/javastacks/spring-boot-best-practice设计思路如果对AOP有一些印象的话,最好的办法就是应用aop实现: 1、定义业务操作日志注解,注解内能够定义一些属性,如操作性能名称、性能的形容等; 2、把业务操作日志注解标记在须要进行业务操作记录的办法上(在理论业务中,一些简略的业务查问行为通常没有必要记录); 3、定义切入点,编写切面:切入点就是标记了业务操作日志注解的指标办法;切面的次要逻辑就是保留业务操作日志信息; Spring AOPAOP (Aspect Orient Programming),直译过去就是 面向切面编程,AOP 是一种编程思维,是面向对象编程(OOP)的一种补充。面向切面编程,实现在不批改源代码的状况下给程序动静对立增加额定性能的一种技术,AOP能够拦挡指定的办法并且对办法加强,而且无需侵入到业务代码中,使业务与非业务解决逻辑拆散; ...

June 29, 2023 · 3 min · jiezi

关于java:后端面经JavaAQS详解

1. AQS是什么?AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如罕用的ReentrantLock。简略来说,AQS定义了一套框架,来实现同步类。 2. AQS核心思想2.1 根本框架AQS的核心思想是对于共享资源,保护一个双端队列来治理线程,队列中的线程顺次获取资源,获取不到的线程进入队列期待,直到资源开释,队列中的线程顺次获取资源。AQS的根本框架如图所示: 2.1.1 资源statestate变量示意共享资源,通常是int类型。 拜访办法 state类型用户无奈间接进行批改,而须要借助于AQS提供的办法进行批改,即getState()、setState()、compareAndSetState()等。拜访类型 AQS定义了两种资源拜访类型: 独占(Exclusive):一个工夫点资源只能由一个线程占用;共享(Share):一个工夫点资源能够被多个线程共用。2.1.2 CLH双向队列CLH队列是一种基于逻辑队列非线程饥饿的自旋偏心锁,具体介绍可参考此篇博客。CLH中每个节点都示意一个线程,处于头部的节点获取资源,而其余资源则期待。 节点构造 Node类源码如下所示: static final class Node { // 模式,分为共享与独占 // 共享模式 static final Node SHARED = new Node(); // 独占模式 static final Node EXCLUSIVE = null; // 结点状态 // CANCELLED,值为1,示意以后的线程被勾销 // SIGNAL,值为-1,示意以后节点的后继节点蕴含的线程须要运行,也就是unpark // CONDITION,值为-2,示意以后节点在期待condition,也就是在condition队列中 // PROPAGATE,值为-3,示意以后场景下后续的acquireShared可能得以执行 // 值为0,示意以后节点在sync队列中,期待着获取锁 static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final int PROPAGATE = -3; // 结点状态 volatile int waitStatus; // 前驱结点 volatile Node prev; // 后继结点 volatile Node next; // 结点所对应的线程 volatile Thread thread; // 下一个期待者 Node nextWaiter; // 结点是否在共享模式下期待 final boolean isShared() { return nextWaiter == SHARED; } // 获取前驱结点,若前驱结点为空,抛出异样 final Node predecessor() throws NullPointerException { // 保留前驱结点 Node p = prev; if (p == null) // 前驱结点为空,抛出异样 throw new NullPointerException(); else // 前驱结点不为空,返回 return p; } // 无参构造方法 Node() { // Used to establish initial head or SHARED marker } // 构造方法 Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } // 构造方法 Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; }}Node的办法和属性值如图所示: ...

June 29, 2023 · 4 min · jiezi

关于java:Spring-FrameWork从入门到NB-单例Bean生命周期

对于Spring为什么要采纳三级缓存架构解决循环依赖这个问题,Spring官网并没有给出阐明,也没有找到设计者的相干设计文档。钻研这个问题其实就是对着代码实现猜想设计用意,所以网上就有了各种猜想,并不完全一致。 钻研这个问题有什么意义吗?集体认为其实也就是学习一下大佬的设计思维从而晋升或者潜在晋升集体能力,除此之外,不会对你应用Spring产生任何影响。 然而据说当初好多面试官会问这个问题,那就和你的切身利益高度相干了,不论是哪一种解释,你终归是须要筹备一套逻辑清晰、可能解释分明说得明确的说法的。 所以咱们也尝试钻研一下这个问题,如果能钻研分明的话,没准能够帮你保命。 Spring为什么要用三级缓存?Spring通过三级缓存解决循环依赖,其中一级缓存是真正的Spring IoC容器,用来存储最终实现创立的bean,这个是必不可少的(其实不应该叫“缓存”,应该叫IoC容器)。 二级缓存是用来在创立过程中缓存对象的,比方对象A的创立过程中须要依赖对象B,然而对象B尚未创立,所以就必须要首先创建对象B,这个时候对象A还没有实现最终的创立,所以必须找个中央把他缓存起来,因而二级缓存看似也很必要。 那咱们能不能不要二级缓存,创立B的时候先把A间接放入一级缓存,即便A不是最终状态,然而后续的逻辑究竟是会实现A的创立、使A变为最终状态的。 那到底为什么非要这个三级缓存呢? 搞清楚这个问题之前,有必要初步理解一下Spring单例bean的生命周期。 Spring单例Bean的生命周期Spring单例Bean的生命周期这个话题其实是有点简单的,咱们当初还不想深刻到这个层面,因而咱们临时不会深入研究生命周期中的每一个过程,只是大略晓得Spring的单例Bean创立过程中都包含那些重要节点,每一个节点大略要干啥,就能够了。 插入一点点题外话:Spring之所以这么弱小的一个重要起因就是他的PostProcessors,Spring不仅仅是通过反转管制的形式创立了一个IoC容器帮你治理Bean,更重要的是他能够通过各种PostProcessors实现各种各样的性能,其中最重要的就是AOP。 好的接下来进入正题。 Spring单例Bean的创立过程也就是Spring源码中doCreateBean的的逻辑。 Spring依照如下程序创立bean实例: applyBeanPostProcessorsBeforeInstantiation实例化前的后置处理器。这个时候指标Bean还没有实例化。实例化前的后置处理器通过CustomTargetSource配置,配置之后Spring将bean实例的创立权交给用户,其实就是Spring不负责bean的创立了。个人感觉应该很少有实用的场景,咱们还是要剖析我的项目中最常见的场景,所以这部分能够疏忽。 多说一句,实例化前和实例化后的后置处理器(BeforeInstantiation和AfterInstantiation)是InstantiationAwareBeanPostProcessor接口(BeanPostProcessor的子接口)中定义的,类定义的javaDoc中原本就说是Spring Framework外部应用的,不倡议用户间接应用。 This interface is a special purpose interface, mainly for internal use within the framework. It is recommended to implement the plain {@link BeanPostProcessor} interface as far as possible.applyBeanPostProcessorsAfterInitialization初始化后的BeanPostProcessor解决,指标Bean必须曾经被BeanPostProcessorsBeforeInstantiation创立。如果实例曾经被创立,打标签beforeInstantiationResolved=true。同上,依赖于第1步的BeforeInstantiation后置处理器,疏忽。createBeanInstance 创立Bean实例。populateBean 属性填充。applyBeanPostProcessorsBeforeInitialization 初始化前的BeanPostProcessors。 对于实现了BeanPostProcessor并注册到以后容器中的所有BeanPostProcessor,调用其办法postProcessBeforeInitialization。和咱们明天的主题关系不大,临时疏忽。invokeInitMethods 调用配置的init-method,或者如果bean实现了InitializingBean接口的话则调用afterPropertiesSet办法,执行初始化。applyBeanPostProcessorsAfterInitialization 初始化后的BeanPostProcessors。调用所有实现了BeanPostProcessor接口并注册到以后容器的BeanPostProcessor,调用其postProcessAfterInitialization办法。以上7步,实现bean的创立。 咱们重点阐明一下第7步,调用BeanPostProcessor的postProcessAfterInitialization办法。咱们晓得Spring的很多重要性能都是通过BeanPostProcessor实现的,其中就包含AOP。Spring中有几个不同的BeanPostProcessor实现AOP,包含: 他们都是虚构类AbstractAutoProxyCreator的扩大类,而AbstractAutoProxyCreator的接口继承关系如下: AbstractAutoProxyCreator - >SmartInstantiationAwareBeanPostProcessor->InstantiationAwareBeanPostProcessor->BeanPostProcessor。 能够看到AbstractAutoProxyCreator实现了的BeanPostProcessor接口,所以他的postProcessAfterInitialization办法最终会被以上的第7步调用到: @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }wrapIfNecessary的作用是,判断以后对象如果须要AOP的话,应用CGLIB创立以后对象的代理对象后返回。 ...

June 28, 2023 · 2 min · jiezi

关于java:Java-8使用Stream来遍历Map的几种方式

在Java 8中,能够应用Stream来遍历Map。以下是一些示例代码: 1.遍历Map的键: Map<String, Integer> map = new HashMap<>();map.put("A", 1);map.put("B", 2);map.put("C", 3);map.keySet().forEach(key -> System.out.println(key));2.遍历Map的值: Map<String, Integer> map = new HashMap<>();map.put("A", 1);map.put("B", 2);map.put("C", 3);map.values().forEach(value -> System.out.println(value));3.遍历Map的键值对: Map<String, Integer> map = new HashMap<>();map.put("A", 1);map.put("B", 2);map.put("C", 3);map.forEach((key, value) -> System.out.println(key + " : " + value));4.应用Stream的形式遍历Map的键值对: Map<String, Integer> map = new HashMap<>();map.put("A", 1);map.put("B", 2);map.put("C", 3);map.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + " : " + entry.getValue()));以上是几种常见的遍历Map的办法,在Java 8中,应用Stream能够更加简洁和不便地遍历Map。

June 28, 2023 · 1 min · jiezi

关于java:Java-8使用Stream-API将List中的对象元素遍历出来并放入Map中

在Java 8中,能够应用Stream API将List中的对象元素遍历进去并放入Map中。上面是一种常见的形式: 假如有一个蕴含Person对象的List,每个Person对象都有惟一的ID和对应的姓名。咱们想要将这些Person对象遍历进去,并依据ID作为Key,姓名作为Value,放入一个Map中。 首先,确保Person类具备getId()和getName()办法,用于获取ID和姓名。 而后,应用Stream的collect()办法,联合Collectors.toMap()办法,能够将List中的对象元素依照指定的Key和Value映射关系收集到Map中。 以下是示例代码: import java.util.*;import java.util.stream.Collectors;class Person { private int id; private String name; public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; }}public class Main { public static void main(String[] args) { List<Person> personList = Arrays.asList( new Person(1, "John"), new Person(2, "Alice"), new Person(3, "Bob") ); Map<Integer, String> idToNameMap = personList.stream() .collect(Collectors.toMap(Person::getId, Person::getName)); System.out.println(idToNameMap); }}运行以上代码,输入后果为: ...

June 28, 2023 · 1 min · jiezi

关于java:Java-8使用Stream-API将Map中的对象元素遍历出来并根据条件过滤元素放入List中

在Java 8中,能够应用Stream API将Map中的对象元素遍历进去并依据条件过滤,而后将符合条件的元素放入List中。上面是一种常见的形式: 假如有一个Map,其中蕴含Person对象作为值,每个Person对象有惟一的ID和对应的姓名。咱们想要遍历这个Map,并过滤出ID大于等于2的Person对象,而后将这些Person对象放入一个List中。 首先,确保Person类具备getId()和getName()办法,用于获取ID和姓名。 而后,应用Map的entrySet()办法获取Map中的所有键值对(Entry对象),再应用Stream API进行遍历和过滤操作。 以下是示例代码: javaimport java.util.*;import java.util.stream.Collectors;class Person { private int id; private String name; public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; }}public class Main { public static void main(String[] args) { Map<Integer, Person> personMap = new HashMap<>(); personMap.put(1, new Person(1, "John")); personMap.put(2, new Person(2, "Alice")); personMap.put(3, new Person(3, "Bob")); personMap.put(4, new Person(4, "David")); List<Person> filteredList = personMap.entrySet().stream() .filter(entry -> entry.getKey() >= 2) .map(Map.Entry::getValue) .collect(Collectors.toList()); System.out.println(filteredList); }}运行以上代码,输入后果为: ...

June 28, 2023 · 1 min · jiezi

关于java:Spring-赌上未来一击推出响应式框架-WebFlux代码更优雅性能更强

Spring-webflux简介spring-webflux是spring在5.0版本后提供的一套响应式编程格调的web开发框架,大量测评证实,应用WebFlux开发接口可能大幅晋升接口的吞吐量。 这个框架蕴含了spring-framework和spring mvc,它能够运行在Netty、Undertow以及3.1版本以上的Serlvet容器上。你能够在我的项目中同时应用spring-webmvc和spring-webflux,或者只用其中一个来开发web利用。 什么是“响应式”所谓响应式,举个例子,当调用一个api获取数据时,无需阻塞期待数据返回,而是当有数据返回时会进行告知。可见响应式是非阻塞的,意味着调用办法后,CPU能够去做别的事件,当接管到数据响应时CPU再回来解决,这种形式进步了零碎的吞吐量。 而响应式编程,其实是为这种异步非阻塞的流式编程制订的一套规范。流式编程已不生疏了,Java8提供的stream api就是这种格调。这套规范包含对运行环境(JVM、JavaScript)以及网络协议相干的标准。 Spring-webflux的响应式APISpring-webflux框架是基于Reactor这个开源我的项目开发的。Reactor框架是跟Spring紧密配合的。 它提供了两种API类型,别离是Mono和Flux; // Mono个别作用于单个对象Mono<Person> person = personDao.getPerson(personId);// Flux个别作用于多个对象Flux<Person> people = personDao.listAllPeople();只管webflux框架基于Reactor,它也能与其余的响应式框架同时应用,比方RxJava。 举荐一个开源收费的 Spring Boot 实战我的项目: https://github.com/javastacks/spring-boot-best-practice抉择Spring-webmvc还是Spring-webflux呢这两个web框架别离代表着两种不同类型的编程流派,官网给出了一个图作为比照如下 依据官网的倡议有以下几点能够作为参考: 如果你曾经应用了Spring-webmvc进行开发,并且我的项目运行良好,就无需更改了;何况当初大多数的三方库都是阻塞的,并不能施展出非阻塞的劣势。webflux提供了相当多的抉择;在服务层,能够应用(Netty, Tomcat, Jetty, Undertow, 和3.1版本以上的Servlet容器)作为web服务;在应用层,能够抉择用@Controller定义还是应用函数编程定义;在编程格调上,能够抉择用Reactor、RxJava或其余。如果你钟爱Java8提供的lambda表达式这种轻量级、函数式的编程格调,那么倡议抉择用webflux;同时对于一些轻量级利用,或者复杂度比拟低的微服务,倡议应用webflux以便更好的进行管制。在微服务架构中,能够将webmvc和webflux我的项目混合应用。两个框架都能够应用@Controller这种注解的形式,使得我的项目的重用更加容易。评估一个我的项目是否应该抉择webflux的最简略的形式是,根据我的项目中是否会应用很多的阻塞API,比方JDBC或者一些阻塞式的API就不实用与webflux我的项目。如果一个webmvc我的项目中有很多的内部零碎调用,能够试试响应式的WebClient,它能间接从Controller的办法中返回响应式后果。响应式编程的学习路线是比拟平缓的,所以如果你身在一个大型的团队中,要思考投入的老本;不过能够用用WebClient来体验下响应式编程。Spring-webflux不仅能够反对在Tomcat、Jetty以及3.1版本以上的Servlet容器上,还可能运行在非Servlet的服务器之上,比方Netty、Undertow等。 应用Springboot构建一个webflux利用,默认就是应用Netty,因为Netty自身就是非阻塞式的实现。 并发模型只管webmvc和webflux都反对应用注解来定义一个Controller,然而其实现形式齐全不同。 webmvc是一个Servlet利用,实现是阻塞式IO,其保护一个线程池来解决每一个用户申请,也就是当Servlet容器启动时,就会创立比方10个线程进去,因而零碎吞吐量的瓶颈在于无限的连接数和阻塞的申请处理过程。 webflux能够基于netty这样的NIO网络框架,它只须要很少的几个工作线程(Event loop worker)就可能解决并响应申请。因为无需阻塞期待办法返回,CPU资源就失去了更好的利用。 webflux并不能让程序运行地更快;而是进步了并发解决申请的能力,即进步了零碎吞吐量。 webflux代码示例Talk is cheap, show me the code上面让咱们来看一下webflux的示例,总的来说应用上是十分便捷的。 咱们用Springboot构建一个webflux利用非常简单,仅仅须要退出这么一个依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId></dependency>首先定义一个对象 public class Person { private Integer id; private Integer age; private String name;}而后定义“PersonController”,响应式格调中不再应用@RequestMapping申明地址映射了,而是通过RouterFunctions.route().GET()办法 @Configurationpublic class PersonRouter { @Resource private PersonHandler personHandler; @Bean public RouterFunction<ServerResponse> personRoutes() { return RouterFunctions.route() .GET("/person/{id}", RequestPredicates.accept(MediaType.APPLICATION_JSON), personHandler::getPerson) .GET("/person", RequestPredicates.accept(MediaType.APPLICATION_JSON), personHandler::listPeople) .POST("/person", personHandler::createPerson) .build(); }}在PersonHandler中解决对应的HTTP申请,等同于MVC架构中的Service层 ...

June 28, 2023 · 1 min · jiezi

关于java:后端面经JavaSynchronize和ReentrantLock区别

1. 概念介绍1.1 线程平安锁Synchronize(同步锁)和ReentrantLock(可重入锁)都是Java中的罕用锁,都是用来保障线程平安的。两者都是同步锁,且都是阻塞同步。 阻塞同步:当一个线程获取锁后,其余线程只能期待(进入阻塞态),期待获取锁的线程开释锁后,其余线程能力获取锁。 1.2 偏心锁在多个线程申请资源的时候,如果依据线程申请的程序来分配资源,那么这个锁就是偏心锁,反之就是非偏心锁。 1.3 响应中断/期待可中断在线程阻塞期间,阻塞线程期待资源的应用,如果这种期待状态可能被中断,从而开释线程去做其余的事件,这就称为期待可中断。 2. 区别2.1 底层实现Synchronize是Java的关键字,ReentrantLock是Java类。因而,Synchronize是JVM层面语法层面的同步锁,ReentrantLock是API层面的同步锁 2.2 锁的用法设置锁和开释锁:Synchroinze是主动加锁和开释锁的,ReentrantLock设置和开释都须要手动操作;润饰的对象:Synchroinze能够润饰办法和代码块,ReentrantLock只能润饰代码块如下代码展现了它们的具体用法: Synchronize作为关键字润饰办法或者代码块即可; //synchronized润饰一个办法时,这个办法叫同步办法。public synchronized void test() {//办法体``}synchronized(Object) {//括号中示意须要锁的对象.//线程执行的时候会对Object上锁}ReentrantLock的应用须要先创立锁对象,而后在须要加锁的代码块中调用lock()办法加锁,应用结束后调用unlock()办法开释锁。 public class LockExample { // 创立锁对象 private final ReentrantLock lock = new ReentrantLock(); public void method() { // 加锁操作 lock.lock(); try { // ... } finally { // 开释锁 lock.unlock(); } }}2.3 锁的特点ReentrantLock相比于Synchronize,有以下特点: 响应中断:ReentrantLock能够响应中断,也就是在其余线程阻塞期间,能够在长时间无奈获取响应的状况下,自行中断期待状态偏心锁:ReentrantLock默认是不偏心锁,然而能够设置为偏心锁,也就是能够依据线程申请的程序来分配资源绑定多个条件:一个ReentrantLock能够同时绑定多个条件,而一个Synchronize锁只能绑定一个条件。这些特点都是Synchronize锁所不具备的。 2.4 性能比拟Sychronize是JVM层面的同步锁,实现简略,性能较好;ReentrantLock是API层面的同步锁,须要手动创立和开释锁,性能较差,然而减少了很多特点,灵活性较好。 2.5 实用场景Synchronize实用于: 简略的同步场景;对同步性能有较高要求不须要响应中断、偏心锁、绑定多个条件;ReentrantLock实用于: 需要更简单的同步场景对性能要求较低须要响应中断、偏心锁、绑定多个条件;3. 总结比拟对上述内容进行总结,如下表所示: 参考文献Synchronize和ReentrantLock区别面试突击:synchronized和ReentrantLock有什么区别?Java 中 synchronized 和 ReentrantLock 的区别

June 28, 2023 · 1 min · jiezi

关于java:Redis跳跃表是如何添加元素的

明天分享的这道题来自于蔚来的实在面试题。 Java 面试不可能不问 Redis,问到 Redis 不可能不问 Redis 的罕用数据类型,问到 Redis 的罕用数据类型,不可能不问跳跃表,当问到跳跃表常常会被问到跳跃表的查问和增加流程,所以接下来咱们一起来看这道题的答案吧。 Redis 有序汇合 ZSet 是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。 压缩列表 ziplist 实质上就是一个字节数组,是 Redis 为了节约内存而设计的一种线性数据结构,能够蕴含多个元素,每个元素能够是一个字节数组或一个整数。跳跃表 skiplist 是一种有序数据结构,它通过在每个节点中维持多个指向其余节点的指针,从而达到快速访问节点的目标。跳跃表反对均匀 O(logN)、最坏 O(N) 复杂度的节点查找,还能够通过程序性操作来批量解决节点。 跳跃表介绍跳跃表 Skip List,也称之为跳表,是一种数据结构,用于在有序元素的汇合中进行高效的查找操作。它通过增加多层链表的形式,提供了一种以空间换工夫的形式来减速查找。 跳跃表由一个带有多层节点的链表组成,每一层都是原始链表的一个子集。最底层是一个残缺的有序链表,蕴含所有元素。每个更高层级都是下层级的子集,通过增加额定的指针来跳过一些元素。这些额定的指针称为“跳跃指针”,它们容许快速访问更远的节点,从而缩小了查找所需的比拟次数。 跳跃表的均匀查找时间复杂度为 O(log n),其中 n 是元素的数量。这使得它比一般的有序链表具备更快的查找性能,并且与均衡二叉搜寻树(如红黑树)相比,实现起来更为简略。 简略的跳跃表如下图所示: 跳跃表增加流程前置常识:节点随机层数在开始讲跳跃表的增加流程之前,必须先搞懂一个概念:节点的随机层数。所谓的随机层数指的是每次增加节点之前,会学生成以后节点的随机层数,依据生成的随机层数来决定将以后节点存在几层链表中。 为什么要这样设计呢?这样设计的目标是为了保障 Redis 的执行效率。 为什么要生成随机层数,而不是制订一个固定的规定,比方下层节点是上层逾越两个节点的链表组成,如下图所示: 如果制订了规定,那么就须要在增加或删除时,为了满足其规定,做额定的解决,比方增加了一个新节点,如下图所示:这样就不满足制订的下层节点逾越上层两个节点的规定了,就须要额定的调整下层中的所有节点,这样程序的效率就升高了,所以应用随机层数,不强制制订规定,这样就不须要进行额定的操作,从而也就不会占用服务执行的工夫了。 增加流程Redis 中跳跃表的增加流程如下图所示: 第一个元素增加到最底层的有序链表中(最底层存储了所有元素数据)。第二个元素生成的随机层数是 2,所以再减少 1 层,并将此元素存储在第 1 层和最低层。第三个元素生成的随机层数是 4,所以再减少 2 层,整个跳跃表变成了 4 层,将此元素保留到所有层中。第四个元素生成的随机层数是 1,所以把它按程序保留到最初一层中即可。其余新增节点以此类推。 随机层数源码剖析随机层数的源码在 t_zset.c/zslRandomLevel(void) 中,如下所示: int zslRandomLevel(void) { int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;}从源码可知,随机层数有 50% 的概率被调配到 Level 1,25% 的概率被调配到 Level 2,12.5% 的概率被调配到 Level 3,以此类推。 ...

June 27, 2023 · 1 min · jiezi

关于java:java8如何遍历List

在Java 8中,能够应用Stream API对List进行遍历。上面是几种遍历List的形式:1.应用forEach办法进行遍历:List<String> list = Arrays.asList("A", "B", "C");list.forEach(element -> { // 解决元素 System.out.println(element);});2.应用Stream API的forEach办法进行遍历:List<String> list = Arrays.asList("A", "B", "C");list.stream().forEach(element -> { // 解决元素 System.out.println(element);});3.应用Stream API的forEachOrdered办法进行有序遍历:List<String> list = Arrays.asList("A", "B", "C");list.stream().forEachOrdered(element -> { // 解决元素 System.out.println(element);});4.应用迭代器(Iterator)进行遍历:List<String> list = Arrays.asList("A", "B", "C");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) { String element = iterator.next(); // 解决元素 System.out.println(element);}留神,应用Stream API进行遍历能够实现函数式编程格调,并且能够利用多核处理器进行并行操作。同时,Stream API还提供了丰盛的两头操作和终端操作,能够进行过滤、映射、排序等操作。 依据具体的需要,能够抉择适宜的形式来遍历List。

June 27, 2023 · 1 min · jiezi

关于java:Java-代码提交影响分析

背景随着业务的日趋简单,做全量回归也越来越艰难为了更加准确的定位后盾我的项目改变所造成的影响,回归范畴划定准确,晋升测试效率须要对Java代码提交影响面进行剖析 调研除了大量相干英文文献,未从Google文献和github查找相干开源成熟的技术和计划。 文献:Chianti: A Tool for Change Impact Analysis of Java Programs 通过文献,联合集体开发教训,决定自主实现,次要面临的问题是Java文件解析和diff信息解析。 通过Google搜寻找到了python相干的解析库,遂决定应用python开发此性能。 实现大抵原理同Idea的Find Usage统一,通过代码改变定位代码影响,并一直遍历受影响的类和办法直至找到最上层的controller层 代码次要由python编写,次要波及2个库: javalang java文件语法解析库unidiff git diff信息解析库通过javalang语法解析获取每个Java文件的import class extends implements declarators methods 等信息 通过unidiff 解析git diff信息(diff file, added_line_num, removed_lin_num) 而后依据文件增删的代码行去判断影响了哪些类和办法,一直遍历受影响的类和办法直至找到最上层的controller层 通过传入我的项目git地址 分支 两次的commit id,即可剖析出两次commit id之间代码改变所带来的影响,并生成树图数据不便展现影响链路。 成果工具集成至公司品质平台,运行成果如下 树形图 总结具体实现已封装上传pypi,欢送大家试用,提供意见反馈 jcci

June 27, 2023 · 1 min · jiezi

关于java:Java基础语法二

二进制只有0,1。依照逢2进1的形式示意数据。 这里能够从0-5几个十进制数转换为二进制数开始学习。 0 --> 01 --> 12 --> 103 --> 114 --> 1005 --> 1016 --> 110除了下面逢二进一这种算法以外,还能够采纳除二取余法。 所谓的除二取余,就是用2一直的去除十进制的数,失去每一步的余数,而后将所有的余数依照逆序排列即是二进制数。计算机中的数据最小单位一般来说,在计算机中会用8个二进制位为一组形成计算机中最小的组成单元,也就是字节(byte,简称B)。 其中每个二进制位称之为一位(bit, 简称b), 1byte = 8bit ,简写1B=8b。 这里以数字6为例,在计算机中用二进制示意,如下: 字符在计算机中是如何存储的 Ascii编码表:即美国信息替换规范编码,规定了古代英语、数字字符和其余西欧字符对应的数字编号。 计算机通过编码表,将字符转换为数字,数字再转换为二进制进行存储。 二进制转十进制二进制转换成十进制,能够通过如下的形式进行计算: 下面的图片中,次要是用每一个二进制数 乘以2的n次幂,最终后果相加即为十进制数。 再比方上面的示例: 八进制和十六进制为了不便察看和示意,推出了八进制和十六进制。 这里以十进制数字97为例: 在上图中,97的二进制示意为01100001,为了便于示意,能够把转换成的二进制数字,从右向左每三位分一组,也就变成了01、100、001,而后把这三组数字假设为独立的二进制数,那么三组二进制数别离对应的数字为1、4、1,将这三个数字组合到一起,即为八进制数。 在八进制中,每3位二进制作为一个单元,最小数是0,最大数是7,共8个数字,这就是八进制。再来说十六进制,每4位二进制作为一个单元,最小数是0,最大数是15,共16个数字,顺次用:0-9 A B C D E F代表就是十六进制。 例如: Java程序中反对二进制、八进制以及十六进制,别离须要以0B或者0b、0、0x或者0X结尾示意。例如: // 二进制int num1 = 0b111;System.out.println(num1); // 7// 八进制int num2 = 0141;System.out.println(num2); // 97// 十六进制int num3 = 0xFA;System.out.println(num3); // 250数据单位计算机存储最小的组成单位是字节,1byte等于8bit,也就是1B=8b。 ...

June 27, 2023 · 1 min · jiezi

关于java:Solon-Web-也支持响应式开发了

"solon.web.flux" 是 solon v2.3.6 新推出的生态插件,为 solon web 提供响应式接口反对 (io.projectreactor) 。为什么叫这个名呢?因为用户群里投票说,这个名大家一看就晓得是干啥的! 这个插件,反对所有 solon 已适配的 http server 插件,包含 jdk 自带的 sun http server。状况如下: 插件适配框架包大小信号协定反对响应式反对备注  solon.boot.jdkhttpjdk-httpserver (bio)0.2Mbhttp反对摸拟了异步机制  solon.boot.jlhttpjlhttp (bio)0.3Mbhttp反对摸拟了异步机制  solon.boot.smarthttpsmart-http (aio)0.7Mbhttp, ws反对   solon.boot.jettyjetty (nio)2.2Mbhttp, ws反对   solon.boot.undertowundertow (nio)4.5Mbhttp, ws反对 因为应用的是“io.projectreactor:reactor-core” 的接口,所以 io.projectreactor 的相干组件都不便用,比方: io.projectreactor.rabbitmq:reactor-rabbitmqio.projectreactor.kafka:reactor-kafka等等...也能够对接其它响应式接口,比方 vert.x 的组件,简略转换后也可应用。 性能状况目前还没有零碎测试过。不过,在一个用户的测试案例里: "solon.boot.jlhttp" 和 "solon.boot.smarthttp" 体现得比 vertx 好很多(有点小意外)"solon.boot.jetty" 却比 vertx 差很多(一时,还没明确为什么是这样)当然 vertx 必定是更业余的,人家封装了一批的响应式组件。Solon 是能够用很小的包去反对响应式,又是有本人的 Bean 容器。而且能够自在切换不同的 http server 适配组件,不同的利用场景,应该会有不同的最优选。 响应式的简略示例这个能力,与原来的 Mvc 并不抵触(能够说是原来 Mvc 能力的扩大)。能够一起用,按需抉择。比方上面这样: @Controllerpublic class DemoController { //经典的套路 @Mapping("/hello1") public String hello1(String name) { return "Hello " + name; } //响应式的套路 @Mapping("/hello2") public Mono<String> hello2(String name) { return Mono.just("Hello " + name); }}总体上跟原来的开发区别不大,只是返回类型变了。 ...

June 27, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-三级缓存解决循环依赖内幕-二

开始用上一篇文章讲到的Spring依赖注入的步骤,用两个例子来推导一下整个过程,举例子有助于理解假相。 先用一个最简略的例子:没有依赖的单例bean的创立。 推导过程是须要上一篇文章的步骤的,要参照步骤一步一步来。 无依赖的单例Bean的创立假如要创立单例bean A: 首先,getBean->deGetBean办法,调用getSingleton(beanName,true)办法。getSingleton(beanName,true)一次查看一级、二级、三级缓存,都没有A对象,返回null。查看Dependon,假如没有设置,不须要创立DependOn对象。调用getSingleton(beanName,factory)办法:查看一级缓存中不存在,将以后bean的name放入“正在创立中”列表,调用createBean创立bean。createBean创立A的实例,查看到以后bean在“正在创立中”列表中,则将以后bean放入三级缓存中。调用populateBean进行属性填充,因为A对象没有依赖任何对象,所以不须要注入其余对象,间接实现属性填充。调用返回到第4步getSingleton(beanName,factory)办法中,实现bean创立后,将以后bean name从“正在创立中”列表中移除。将bean A从三级缓存、二级缓存中移除,放入一级缓存中。实现bean A的创立。循环依赖的单例Bean的创立过程A依赖B,B依赖A,假如都是属性的相互依赖,即: @Componentpublic class A { @Autowired private B b;}@Componentpublic class B { @Autowired private A a;}集体认为简单的循环依赖都能够转化为我依赖你、你依赖我这种模式,所以咱们还是试图把这个例子说的分明明确一点,关键步骤用图示的形式阐明。 假如首先创立A实例。 执行到getSingleton(beanName,boolean)办法的时候,三个缓存都空:而后,接着执行到doCreateBean的时候,会把A的工厂办法放入到三级缓存,如图:接下来就是populateBean办法,为bean A执行属性填充,查找到须要注入bean B,调用getBean,如图,用蓝色箭头示意,当执行到getSingleton(beanName,boolean)办法的时候,三个缓存中都没有bean B:继续执行到doCreateBean的时候,会把B的工厂办法放入到三级缓存:而后到populateBean办法,为bean B执行属性填充,查找到须要注入bean A。调用getBean(A),如图,用红色箭头示意,当执行到getSingleton(beanName,boolean)办法的时候,三级缓存中存在bean A,所以,利用工厂办法创立A对象,放入二级缓存,返回bean A,并将A从三级缓存移除:返回到步骤5,将bean A赋值给bean B的属性a,实现bean B的属性填充。之后返回到第4步,实现bean B的doCreateBean办法,将Bean B放入一级缓存,同时将Bean B从二级、三级缓存移除:这一步实现之后,Bean B实现了创立,也实现了他的属性A的依赖注入,然而注入的Bean A是个半成品,还放在二级缓存中,尚未实现创立。 然而因为Bean A的创立流程还没有完结,所以不会有问题,接下来的步骤会实现Bean A的创立。 流程返回到第3部,bean A的populateBean办法获取到了曾经实现创立的bean B对象,实现Bean A的属性注入。之后继续执行bean A的doCreateBean的后续逻辑,实现Bean A的创立,将Bean A放入到一级缓存,并从二级、三级缓存移除:实现bean A和bean B的创立,实现A、B之间的依赖注入。小结用图解的形式阐明Spring通过三级缓存解决依赖注入的过程。其实集体了解,对于更加简单的依赖关系,注入过程无非就是在以上10个步骤之间一直递归调用的过程。 据说有一个问题是,Spring三级缓存的必要性,前面的文章会尝试答复这个问题。 上一篇 Spring FrameWork从入门到NB -三级缓存解决循环依赖底细 (一)

June 26, 2023 · 1 min · jiezi

关于java:干掉Navicat阿里Chat2DB来了

最近朋友圈被阿里的 Chat2DB 刷屏了,磊哥也是第一工夫下载并体验了阿里巴巴的 Chat2DB,明天就急不可待和大家分享一下。 什么是 Chat2DB?Chat2DB 是一款由阿里巴巴开源收费的多数据库客户端工具,反对 windows、mac 本地装置,也反对服务器端部署,web网页拜访。 和传统的数据库客户端软件 Navicat、DBeaver 相比 Chat2DB 集成了 AIGC 的能力,可能将自然语言转换为 SQL, 也能够将 SQL 转换为自然语言,能够给出研发人员 SQL 的优化倡议,极大的晋升人员的效率,是 AI 时代数据库研发人员的利器, 不懂 SQL 的经营或业务也能够轻松应用疾速查问业务数据、生成报表的能力。 以上定义来自于官网,简略来说,Chat2DB 是一款开源收费的、具备 AI 能力的数据库客户端连贯工具。它反对简直所有支流的数据库,Redis 也有反对哦,很惊喜。 Chat2DB 次要性能Chat2DB 次要蕴含以下 4 个性能: 自然语言转 SQL:输出自然语言,输入查问 SQL。SQL解释:输出 SQL,输入自然语言SQL优化:输出 SQL,输入 SQL 相干的优化倡议SQL转换:输出 SQL,将 SQ L转换为指标类型的 SQL Chat2DB 反对哪些数据库? 下载地址下载地址:https://github.com/chat2db/Chat2DB/blob/main/README_CN.md如下图所示,抉择适合的平台下载并装置即可:Chat2DB 官网:https://github.com/chat2db/Chat2DB 应用体验视频我的 B 站 Chat2DB 体验视频,欢送大家观看和一键三连哈:https://www.bilibili.com/video/BV1TX4y1p7jD/ 小结Chat2DB 总体给我的感觉是还能用。它有两个长处:第一,开源收费、无需破解,反对多平台。第二,反对应用提醒词生成 sql,尽管说生成的 sql 有时候不是特地好,但聊胜于无,有总比没有强。 它的毛病也有三个:第一,不反对在后果集外面间接进行批改操作,不算很不便。第二,进行增加、删除、批改的时候不会返回受影响的行数,只是提醒胜利,不够直观。第三,AI 服务器不稳固,常常超时,并且AI 模型匹配不是很精准,这些都是有待改良的点。 关关惆怅关关过,前路漫漫亦灿灿。成前人未竟之业,我辈持续致力,加油国产软件~

June 26, 2023 · 1 min · jiezi

关于java:分享一些我技术成长的感悟

今晚来聊聊我在技术成长中的一些感悟,跟大家分享下。 BALABALA在大学的时候,我一个计算机专业相干的证书都没考,自认为这些证书对我当前找工作没什么大的帮忙。于是我把工夫更多地花在钻研八股文上,因为八股文在面试的时候是要用到的。 (利益化) 我会对我做的事件利益化,尽管听起来不太好,但我认为是没方法的,我的精力是无限的,我所花的工夫就应该谋求正向回报。 不过这是针对对于咱们的饭碗层面的,如果纯碎是喜好和喜爱干某件事,那就无需关注利益回报了。就比方我喜爱看英雄联盟较量直播,看B站划水摸鱼,看各种热血漫画动漫,这对我来说就是喜好,看了开心,这就足够了。 有不少的同学都去考软件设计师的证书,而我常常跑图书馆里温习着八股文写文章。至于跑图书馆并不是我喜爱,而我是认为人的惰性是始终存在的,我想终日躺床上刷一天B站看电影,但事实通知我这样上来不行。所以我常常去图书馆承受读书气氛的陶冶,试图去战胜这种惰性(这招对我是无效的) (克服惰性) 搞八股文而不去考据,那时我认为本人做得很对,我就应该搞Java,Java才是邪道。 我在网上看到大家都说Java根底很重要,于是我就去钻研各种乌七八糟的八股文:继承+结构器+动态代码块的加载程序、String各种+号运算符StringBuilder配合intern各种姿态打印true还是false等等等各种Java根底相干的口试刁钻题我都搞过一轮。 但起初通过一轮校招之后,我懵了:这个是啥玩意啊,都不考Java根底啊,我前段时间学了个der啊。计算机网络/操作系统/算法才是永远滴神! 很多公司在校招的时都不介意你在学校时用的是什么语言,只有他们认为你根底扎实、头脑清醒灵便,很可能就要你了。校招时的根底扎实并不是针对于Java的,只有你的简历没有适度夸大吹牛本人很懂Java,可能他们压根就不问你Java相干的内容(当然了,一个应届生在他们的眼里,你又能有多懂Java)。 为了找工作,放弃考据转而去学习根底这个抉择我感觉是对的,但方向没找准。 我也不没看人家以往校招面试考查的是什么内容,自认为根底很重要,就去一根筋去搞各种Java的语法。将本人很多的工夫花在在偏门的题里,咋一看如同懂了很多根底的常识,但对找工作这件事件上和后续的职场开发没有啥帮忙。 我之前跟我一个共事聊过,他跟我是同一届的。他说他在大二的时候跟学长聊过,晓得校招重点考查什么内容。而后他就专盯这些内容去搞了整个大学生涯,在校招就是乱杀的。 看到这里,有的人可能就不信了。咋人家搞了一个大学,校招乱杀了。你走了这么多弯路,你还跟人家一个公司。其实很简略,因为我是白菜,人家是SP。 (奋斗的方向要找对) 在找实习之前,我想动手做一个我的项目以便简历上能有些本质的内容。那时候晓得有Elasticsearch这么一个搜索引擎框架,过后我不懂Linux,但我想要用它来做检索。那时我还没折腾过虚拟机,也没买过云服务器。 要上Elasticsearch必定要部署嘛,那部署在哪呢?我一想,必定是Windows啊,我的本地开发环境。我那时候就真这样干了,依稀记得当年的Elasticsearch版本还是2.3吧,我花了我很长的工夫才装置上,踩了很多坑,还有一大堆的问题... 其实我在踩坑的那时是晓得Elasticsearch这种软件应该是要在Linux部署的,但就是硬着在Windows上搞,当初看来就是纯浪费时间。 (及时调整:咱们没方法判断每一次抉择都是对的) why写下这篇文章次要是最近在群里看到有小伙伴在钻研些偏门的八股文(类加载程序/外部类/ture/false),我赞成也钦佩这种折腾行为,当解开答案时,是乏味的,在这个过程中也必定能学会很多骚操作。 但如果是以找工作的规范去掂量这些偏门根底题目,我是不倡议去花工夫折腾的。 当初面试充斥着各种零碎架构千万流量高并发高可用这种话题,其中有不少的确是八股文。但这至多对工作中还是有些许帮忙的,咱们能够借鉴这些八股文去掂量本人所负责的零碎应该是如何设计,有什么样的性能,达到什么规范等等。 可能你入职当前,发现公司的零碎很烂,发现招你进来的人也早曾经晓得你负责的零碎的确很烂。但有没有一种可能,招你进来的人是感觉你可能晓得它烂在哪里,跟业界相比有多大的差距,这或者就是你被录取的理由 如果想学Java我的项目的,强烈推荐我的我的项目音讯推送平台Austin(8K stars),能够用作毕业设计,能够用作校招,能够看看生产环境是怎么推送音讯的。音讯推送平台推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等音讯类型。 https://gitee.com/zhongfucheng/austin/https://github.com/ZhongFuCheng3y/austin

June 25, 2023 · 1 min · jiezi

关于java:Spring-FrameWork从入门到NB-三级缓存解决循环依赖内幕-一

循环依赖就是我依赖你、你依赖我,或者A依赖B、B依赖C、C依赖A......组成的盘根错节的依赖关系。 其实各种不同的依赖关系最终从逻辑上都能够演变为:我依赖你、你依赖我。 循环依赖大抵能够分为两种状况: 属性依赖:比方A对象有一个属性B,B对象有属性A。结构器依赖:A对象的结构器中有参数B,B对象的结构器有参数A。结构器依赖是Spring解决不了的(指的是A、B不存在其余结构器,或者Spring须要通过相互依赖的结构器创立A、B对象),这种死结你本人写代码都没方法解决,Spring当然也没有方法。 Spring通过其特有的三级缓存机制能解决属性依赖。顺便说一下,其实Spring官网或相干材料中并没有提出过三级缓存的说法,也不太分明这种说法从哪儿来(或者是我疏漏了没有查到),反正很NB、很浅近的样子,其实如果你认真读源码,换做一种实在的说法:Spring最终通过三个Map的奇妙应用解决循环依赖问题.....也就没有那么NB、那么浅近、尤其是没有那么高大上了。然而不管怎么说,Spring解决循环依赖的框架还是值得咱们认真学习把握的。因为Spring各路大牛的设计思维的确#@¥%……%*(的确和你的支出会间接相干所以你肯定有必要花点工夫精力彻底搞清楚)。 三级缓存框架先上一张图,对三级缓存有个直观印象: 道歉,画的有点low,不过还是能阐明问题的哈。 Spring IoC容器中当然不止是DefaultSingletonBeanRegistry一个容器,但三级缓存就是针对单例Bena来说的,其实如果只是从Bean容器的角度来讲,Spring IoC容器很大水平上讲的也就是单例Bean的容器,原型Bean原本也是每次应用、每次创立,所以也就不须要缓存。只有单例Bean在Spring IoC容器中是一次创立长期驻留,驻留的中央就是三级缓存中的“一级缓存”。 如上图,三级缓存定义在DefaultSingletonBeanRegistry中: 三级缓存:存储bean name和bean工厂的容器。二级缓存:存储bean name和bean实例,二级缓存中的bean实例刚刚实现实例化、尚未实现属性赋值,所以是半成品。一级缓存:存储最终实现创立的Bean,利用getBean或者主动拆卸的Bean就是从以及缓存获取的。Spring解决循环依赖的具体过程其实认真读源码,钻研完Spring的Bean创立过程之后,循环依赖的原理就高深莫测了(尽管很简单)。 Spring的Bean创立过程相对来说还是比较复杂的,钻研分明全貌还是要花费点工夫的,咱们的策略就是一步一步抽丝剥茧以及各个击破,明天的次要指标就是三级缓存解决循环依赖的过程,所以对其余过程就要疏忽。 Spring源码,Bean的创立过程: AbstractBeanFactory#getBean创立和获取bean的对立入口,会调用doGetBean办法。 AbstractBeanFactory#doGetBean首先调用DefaultSingletonBeanRegistry#getSingleton(beanName,true)办法,留神这个getSingleton办法是不会创立bean的。如果没有拿到Bean,就筹备创立Bean首先查看是否有Dependon,有的话,先创立Dependon如果要创立的是单例bean,则调用getSingleton(beanName,factory)创立bean,留神这个才会创立bean否则,如果是原型bean...和明天的话题无关,临时疏忽DefaultSingletonBeanRegistry#getSingleton(beanName,boolean)咱们下面说过了,这里不会创立bean: 一级缓存存在,则间接返回一级缓存不存在并且Bean正在创立中,从二级缓存获取二级缓存获取到则返回,否则查看三级缓存三级缓存有的话,调用三级缓存的bean工厂加工bean,放入二级缓存后,返回,并革除三级缓存否则三级缓存没有的话返回nullDefaultSingletonBeanRegistry#getSingleton(beanName,Factory)这个getSingleton负担着创立bean实例的工作,如果获取不到的话,会创立: 一级缓存存在,则间接返回否则,以后bean放入“正在创立中”列表(singletonsCurrentlyInCreation)应用Factory创立Bean实例:调用AbstractAutowireCapableBeanFactory#createBean办法以后bean从“正在创立中”列表移出实现创立的Bean从二级、三级缓存移出,放入一级缓存咱们须要留神的是,这个办法调用实现之后,一个残缺的bean就被创立进去、被放入到一级缓存了。 但你要晓得的是,这个办法不会那么容易实现的,玄机就在createBean办法中,如果有依赖的话还会递归调用AbstractBeanFactory#getBean办法的,逻辑又绕回去了...... AbstractAutowireCapableBeanFactory#createBean真正创立Bean的中央: 创立Bean实例(原始类的实例,或者CGLIB代理对象实例)如果是单例Bean并且以后Bean正在创立中,则调用addSingletonFactory:作用是创立一个工厂办法,增加到三级缓存。调用populateBean办法,属性填充(可能会产生循环依赖啊...)须要特地留神的是,三级缓存就是在这儿创立的,存储的内容是一个bean factory、应用lamda表达式创立,工厂办法是getEarlyBeanReference。 getEarlyBeanReference办法前面从三级缓存获取对象的时候会回调,作用是通过SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference办法最终调用到wrapIfNecessary办法,目标是判断bean如果须要AOP的话则生成他的代理对象。 三级缓存存储bean factory、而不是存储bean实例的目标就是为了解决依赖注入过程中的代理对象的解决的。 这部分内容的确有点绕,是须要动动脑子认真仔细思考一下能力搞明确的。 AbstractAutowireCapableBeanFactory#populateBean属性填充,这个过程中解决主动封装,会有依赖注入。 调用AutowiredAnnotationBeanPostProcessor#postProcessProperties办法。 而后,AutowiredAnnotationBeanPostProcessor#postProcessProperties进行属性赋值。 而后,再调用DefaultListableBeanFactory#resolveFieldValue办法。 最初,老套路,DefaultListableBeanFactory#doResolveDependency办法。 办法doResolveDependency中首先解决候选注入对象的问题,这部分代码中蕴含依赖注入是查找对象(DL)的假相,这部分不是明天的主题,跳过。 DL查找到的必定不是待注入的对象,而是待注入对象的beanName。 那猜测一下,拿到待注入的beanName之后,肯定是要拿到bean对象之后再注入的,Spring会怎么解决呢?怎么依据beanName拿到bean对象呢? 其实不难猜到! doResolveDependency办法会调用DependencyDescriptor#resolveCandidate办法,resolveCandidate办法调用beanFactory.getBean获取对象,其实最终调用的是AbstractBeanFactory的getBean办法。 揉一揉昏花的老眼,是否感觉到似曾相识呢? 没错,往上翻,就是咱们开始剖析循环依赖的入口处,又转回去了。 所以没错,Spring IoC在Bean实例化、依赖注入的过程,就是一个一直递归调用的过程。 小结以上,就是Spring IoC的bean的实例化以及依赖注入、三级缓存解决循环依赖的次要逻辑了,整顿一下、用几个例子推导一下,置信就能搞清楚这部分内容了。 你如果不置信的话咱们下一篇文章就来推导一下。 上一篇 Spring FrameWork从入门到NB -classpath扫描和组件托管](https://segmentfault.com/a/1190000043918177)

June 25, 2023 · 1 min · jiezi