生活知识大全图片(生活知识小报图片)真没想到
信息来源:互联网 发布时间:2023-09-22
因此反编译看下 @Builder 注解帮我们生成的代码,可以看到只有一个带全部参数的构造函数供 Builder 类内部类使用,并且该构造函数没有
本期知识小报的主要内容包括:• Lombok 之 @Builder 注解与 JSON 反序列化的冲突• 如何动态设置日志输出级别• JWT的生成原理和误区• Java 8 parallelStream 避坑指南
Lombok 之 @Builder 注解与 JSON 反序列化的冲突Lombok 是 Java 开发中常见的工具,可以极大提高开发效率,对冗余代码进行简化@Builder 是 Lombok 提供的一个快速实现建造者模式的注解。
通过该注解,便可以直接进行参数的链式调用构造, 如下所示:@Builder@DatapublicclassItemData {/** * 商品id */publicLongitemId;/** * 商品标题
*/publicStringitemTitle;}ItemDataitemData= ItemData.builder() .itemId(123456L) .itemTitle("闲小鱼") .build();
一切都看起来非常优雅,直到我们使用 fastjson 进行序列化和反序列化,看似再正常不过的代码,居然抛了如下异常ItemDataitemData= ItemData.builder() .itemId(
123456L) .itemTitle("闲小鱼") .build();//fastjson序列化StringitemString= JSONObject.toJSONString(itemData);
//fastjson反序列化ItemDatadeserializeItem= JSON.parseObject(itemString, ItemData.class);
通过源码分析得知,fastjson 实例化对象优先使用无参构造函数,其次再使用 public 修饰的带参构造函数因此反编译看下 @Builder 注解帮我们生成的代码,可以看到只有一个带全部参数的构造函数供 Builder 类内部类使用,并且该构造函数没有被 public 修饰,因此反序列化时 fastjon 无法进行实例化对象。
针对上述问题,我们在使用 @Builder 注解时,建议配合 @NoArgsConstructor @AllArgsConstructor 这两个注解使用,可以避免一些情况下的的反序列化问题(jackson , Gson 等序列化框架也会有此问题)。
@Builder@Data@NoArgsConstructor@AllArgsConstructorpublicclassItemData {/** * 商品id */publicLongitemId;
/** * 商品标题 */publicStringitemTitle;}如何动态设置日志输出级别日志输出级别定义取决于日志框架,如常见的日志框架Log4j中支持8种日志级别,从低到高依次为OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL;而Logback中支持7种日志级别,从低到高依次为OFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL。
日志打印级别由日志配置文件声明,在应用启动时便根据配置文件指定了相关Logger的日志打印级别那么在应用运行过程中,如何动态变更日志输出级别呢?对于单台机器,可以使用arthas(阿里巴巴开源的超实用JAVA监控诊断工具)来快速的查看和重置指定Logger的日志打印级别。
输入命令logger,查看当前机器上所有Logger的相关信息
输入命令logger --name root --level debug -c 2dde1bff (--name指定Logger名称,--level指定修改到的日志级别) 这里,笔者将主日志日志打印级别调整到了DEBUG。
那么对于线上机器集群,如何实现日志级别动态调整呢?答:通过配置中心 + LoggingSystem(org.springframework.boot.logging.LoggingSystem)来实现LoggingSystem会帮我们屏蔽掉底层具体的日志框架,实现统一的日志操作。
这里,笔者为大家提供一个工具类@ServicepublicclassLoggerLevelAdjustService{@Autowiredprivate LoggingSystem loggingSystem;
/** * 设置主日志(ROOT Logger)的日志输出级别 * @param newLevel 要设置到的日志输出级别 * @return void */publicvoidsetRootLoggerLevel
(String newLevel){ setLoggerLevel(ROOT_LOGGER_NAME, newLevel); }/** * * @param loggerName logger name
* @param newLevel 要设置到的日志输出级别 * @return void */publicvoidsetLoggerLevel(String loggerName, String newLevel)
{if (StringUtils.isEmpty(newLevel)) {return; }for (LogLevel level : LogLevel.values()) {if (level.name().equals(newLevel)) {
loggingSystem.setLogLevel(loggerName, level); } } }}之后,只需要在配置中心接收配置推送的代码中,调用工具类中修改日志输出级别的方法,即可实现日志输出级别的动态调整。
JWT的生成原理和误区JWT的全名是JSON Web Token,是一套去中心化的信息传输工业标准,在认证和信息交换领域中被较为广泛地使用在短时效身份认证场景下,相比于传统的“cookie + session”方案,JWT使用计算替代存储,无需服务端额外存储session,可以减少数据库访问,并具有更好的扩展性。
在结构上JWT由三部分组成:Header,Payload和Signature,并使用“.”将三部分连接形成“Header.Payload.Signature”在Header中,指明Signature生成过程使用的加密算法,明文如下:。
在Payload中,放入需要被传输的信息,如:
在最终的JWT令牌中,Header和Payload会使用Base64Url算法编码为最终传输信息的前二部分为了防止信息交换的过程中被篡改,JWT设计了第三部分SignatureSignature通过将Header和Payload的信息进行加密的方式生成,常见支持的加密算法有HS256(一种引入密钥的摘要算法),非对称加密RS256等。
若我们采用HS256,则Signature的生成公式为:Signature = HS256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 在设定HS256特定密钥后,我们可以得到Signature为zYAABCrTH4trLliHsSbmCA3Rrn0Fym5aZ5MgrlnkW9Y。
最终将编码后的Header、Payload和生成的Signature使用“.”连接起来,便得到了JWT令牌(红色部分为Header,紫色为Payload,蓝色为Signature)
显然,若在传输过程中Payload被进行了篡改,接收方使用预先定义的密钥便会生成截然不同的Signature因此,JWT可以实现信息传输过程中的防篡改此外,因为Payload部分仅仅是进行了编码(而非加密),所以在接收方或者第三方可以被解码为明文userId。
因此,切记不能使用Payload传输密码等敏感信息Java 8 parallelStream 避坑指南StreamAPI 是 Java 8 中引入的新特性,借助这个能力,开发者们可以更容易地对集合数据进行处理。
除了默认的串行 Stream 之外,StreamAPI 还引入了 parallelStream ,只需一行 parallelStream() ,就可以开启多线程并行处理,进而充分发挥多核 CPU 的性能。
不过在你使用它之前,最好注意以下几个问题:1. 并行流与串行流的执行结果可能会不一致IntStream.of(1, 2, 3).reduce(4, Integer::sum); 这是一段简单的数组元素相加,得到执行结果为 10,符合预期。
IntStream.of(1, 2, 3).parallel().reduce(4, Integer::sum);接下来我们将这段代码改写为使用并行流来处理,在其他条件都不变的基础上,得到结果为 18,与预期结果不一致。
造成这种差异的原因主要是 Stream 的并行流底层采用的是 ForkJoinPool 来执行,其线程数为运行机器的 cpu 核数-1(因此cpu核数会影响执行结果),其主要原理是把任务分成多个部分,每个部分进行独立计算最后合并。
复原一下上面的例子,如下图所示:
2. 串行流并不是在所有场景下都可以带来性能提升由上面的例子可以看出,并行流更适合处理无状态的数据,即各子任务之间关联性较弱的操作,如 map,filter 等,而 sorted、 distinct 以及 limit 等操作可能并不会带来性能上的提升,反而会因为线程的额外开销导致效率不如并行流。
因此,需要视具体使用场景来评估是否使用3. 长耗时任务需要单独指定处理任务的线程池parallel stream 默认使用的执行线程池为 ForkJoinPool 内部的静态 commonPool,主要用于处理没有为其单独分配线程池的任务。
该线程池的大小默认为 cpu 核数-1,因此如果在其中运行长耗时的任务,极有可能造成线程池阻塞,影响当前业务甚至其他用了 parallel stream 的业务此时更建议手动开启一个新的线程池进行任务处理,或者为 parallel stream 单独指定一个专用线程池,如下所示:。
ForkJoinPoolbusinessPool=newForkJoinPool(4);intsum= businessPool.submit(() -> IntStream.of(1, 2, 3).parallel().reduce(
0, Integer::sum)).get();businessPool.shutdown();引用Github fastjson: https://github.com/alibaba/fastjson/wiki/ASMDeserializerFactory%E8%AE%BE%E8%AE%A1
Arthas: https://arthas.aliyun.com/doc/logger.htmlJWT: https://jwt.io/introductionJava8Stream: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html
免责声明:本站所有信息均搜集自互联网,并不代表本站观点,本站不对其真实合法性负责。如有信息侵犯了您的权益,请告知,本站将立刻处理。联系QQ:1640731186

