当前位置:首页 > 数码 > 资深开发者的机密战略-SpringBoot中经常使用ObjectMapper的终极指南 (资深开发者的工作内容)

资深开发者的机密战略-SpringBoot中经常使用ObjectMapper的终极指南 (资深开发者的工作内容)

admin5个月前 (05-08)数码23

1.每次new一个

在SpringBoot名目中要成功对象与Json字符串的互转,每次都须要像如下一样new一个ObjectMer对象:

publicUserEntitystring2Obj(Stringjson)throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();returnobjectMapper.readValue(json,UserEntity.class);}publicStringobj2String(UserEntityuserEntity)throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();returnobjectMapper.writeValueAsString(car)}

这样的代码四处可见,有疑问吗?

你要说他有疑问吧,确实能反常口头;可你要说没疑问吧,在谋求性能的同窗眼里,这失实算是罪大恶极的代码了。

首先,让咱们用JMH对这段代码做一个基准测试,让大家对其性能有个大略的了解。

基准测试是指经过设计迷信的测试方法、测试工具和测试系统,成功对一类测试对象的某项性能目的启动定量的和可对比的测试。而JMH是一个用来构建,运转,剖析或其余运转在JVM之上的言语的纳秒/微秒/毫秒/微观级别基准测试的工具。

资深开发者的机密战略
@BenchmarkMode(Mode.Throughput)@OutputTimeUnit(TimeUnit.SECONDS)@State(Scope.Thread)@Fork(1)@Warmup(iterations=5,time=1)@Measurement(iterations=3,time=1)publicclassJsonJMHTest{Stringjson="{"id":122345667,"eml":"jianzh5@163.com","price":12.25}";UserEntityuserEntity=newUserEntity(13345L,"jianzh5@163.com",BigDecimal.valueOf(12.25));/***测试StringtoObject*/@BenchmarkpublicUserEntityobjectMapper2ObjTest()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();returnobjectMapper.readValue(json,UserEntity.class);}/***测试ObjecttoString*/@BenchmarkpublicStringobjectMapper2StringTest()throwsJsonProcessingException{ObjectMapperobjectMapper=newObjectMapper();returnobjectMapper.writeValueAsString(userEntity);}publicstaticvoidmain(String[]args)throwsRunnerException{Optionsopt=newOptionsBuilder().include(JsonJMHTest.class.getSimpleName()).build();newRunner(opt).run();}}

测试环境

#JMHversion:1.36#VMversion:JDK17.0.3,OpenJDK64-BitServerVM,17.0.3+7-LTS#AppleM1/16GB

测试结果

经过测试结果可以看出,每次new一个ObjectMapper,在成功字符串转对象时每秒可以成功23万屡次,而成功对象转Json字符串每秒仅可成功2.7万次。

那该如何优化,优化性能呢?

2.单例化

老鸟们都知道,在创立工具类时要将工具类设置成单例的,这样不只可以保障线程安保,也可以保障在系统全局只能创立一个对象,防止频繁创立对象的老本。

所以,咱们可以在名目中构建一个ObjectMapper的单例类。

@GetterpublicenumObjectMapperInstance{INSTANCE;privatefinalObjectMapperobjectMapper=newObjectMapper();ObjectMapperInstance(){}}

再次经常使用JMH对其测试:

@BenchmarkpublicUserEntitysingleten2ObjTest()throwsJsonProcessingException{ObjectMapperobjectMapper=ObjectMapperInstance.INSTANCE.getObjectMapper();returnobjectMapper.readValue(json,UserEntity.class);}@BenchmarkpublicStringsingleten2StringTest()throwsJsonProcessingException{ObjectMapperobjectMapper=ObjectMapperInstance.INSTANCE.getObjectMapper();returnobjectMapper.writeValueAsString(userEntity);}

测试结果如下:

可以看到,经常使用单例形式,String转对象的方法每秒可以口头420多万次,比newObjectMapper的形式快了18倍;而对象转String的方法每秒可以口头830万次,性能优化了300倍(看到结果的一瞬间我傻眼了,一度疑心是写错代码了)!!!!

3.共性化性能

当然,在名目中经常使用ObjectMapper时,有时刻咱们还须要做一些共性化性能,比如将Long和BigDemical类型的属性都经过字符串格局启动转换,防止前端经常使用时失落数值精度。

这些类型转换的格局映射都可以在单例类中性能,代码如下:

@GetterpublicenumObjectMapperInstance{INSTANCE;privatefinalObjectMapperobjectMapper;ObjectMapperInstance(){objectMapper=newObjectMapper();//注册自定义模块initialize();}privatevoidinitialize(){CustomJsonModulecustomJsonModule=newCustomJsonModule();objectMapper.registerModule(customJsonModule);}}

在initialize()方法中给ObjectMapper注册自定义序列化转换器。

第一行是经常使用注册自定义序列换转换器后的成果,给id和price字段都加上了引号。

再来一次性JMH测试:

可以看到,给ObjectMapper额外注册转换类型以后性能会遭到肯定的影响,但对业务影响不大。(啥业务能这么高的恳求~)

4.小结

经过上方的测试,论断曾经很明晰了。经常使用单例形式启动字符串转对象时性能可以优化18倍,而对象转String性能快了惊人的290万倍,所以在Spring中如何正确的经常使用ObjectMapper不用我再说了吧~


[Kotlin]SpringBoot 获取原生HTTP Request内容实例化model

背景: SpringBoot日常开发中不需要接触原生HTTP Request,框架本身已经实现了request请求自动转化model/vo/dto对象,但有时特殊场景需要获取、操纵原生HTTP Request内容。

Kotlin实现: 在Controller中通过如下方式可以获取到HTTP Request并转为JSON格式

进而,可以获取 jacksonObjectMapper ,然后实例化对应model,此处应该有一些业务逻辑,否则直接通过SpringBoot框架在controller中自动实例化即可。

springboot 中 RedisCacheManager rm = new RedisCacheManager(redisTemplate);我的项目没这个构造

引入redis 开启缓存<dependency><groupId></groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId></groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 配置文件# Redis数据库索引(默认为0)=0# Redis服务器=localhost# Redis服务器连接端口=6379# Redis服务器连接密码(默认为空)=# 连接池最大连接数(使用负值表示没有限制)-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)-wait=-1# 连接池中的最大空闲连接-idle=8# 连接池中的最小空闲连接-idle=0# 连接超时时间(毫秒)=0添加cache的配置类package ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import 2JsonRedisSerializer;@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport{@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();(()());(());for (Object obj : params) {(());}return ();}};}@SuppressWarnings(rawtypes)@Beanpublic CacheManager cacheManager(RedisTemplate redisTemplate) {RedisCacheManager rcm = new RedisCacheManager(redisTemplate);//设置缓存过期时间//(60);//秒return rcm;}@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer();ObjectMapper om = new ObjectMapper();(, );(_FINAL);(om);(jackson2JsonRedisSerializer);();return template;}}RedisServicepackage ;import ;import ;import ;import ;import ;import .*;import ;/***/@Servicepublic class RedisService {@Autowiredprivate RedisTemplate redisTemplate;/**//操作字符串* ();//操作();//操作();//操作list ();//操作有序setset ();*//*** 写入缓存* @param key* @param value* @return*/public boolean set(final String key, Object value) {boolean result = false;try {ValueOperations<Serializable, Object> operations = ();(key, value);result = true;} catch (Exception e) {();}return result;}/*** 写入缓存设置时效时间* @param key* @param value* @return*/public boolean set(final String key, Object value, Long expireTime) {boolean result = false;try {ValueOperations<Serializable, Object> operations = ();(key, value);(key, expireTime, );result = true;} catch (Exception e) {();}return result;}/*** 批量删除对应的value* @param keys*/public void remove(final String... keys) {for (String key : keys) {remove(key);}}/*** 批量删除key* @param pattern*/public void removePattern(final String pattern) {Set<Serializable> keys = (pattern);if (() > 0)(keys);}/*** 删除对应的value* @param key*/public void remove(final String key) {if (exists(key)) {(key);}}/*** 判断缓存中是否有对应的value* @param key* @return*/public boolean exists(final String key) {return (key);}/*** 读取缓存* @param key* @return*/public Object get(final String key) {Object result = null;ValueOperations<Serializable, Object> operations = ();result = (key);return result;}/*** 哈希 添加* @param key* @param hashKey* @param value*/public void hmSet(String key, Object hashKey, Object value){HashOperations<String, Object, Object> hash = ();(key,hashKey,value);}/*** 哈希获取数据* @param key* @param hashKey* @return*/public Object hmGet(String key, Object hashKey){HashOperations<String, Object, Object>hash = ();return (key,hashKey);}/*** 列表添加* @param k* @param v*/public void lPush(String k,Object v){ListOperations<String, Object> list = ();(k,v);}/*** 列表获取* @param k* @param l* @param l1* @return*/public List<Object> lRange(String k, long l, long l1){ListOperations<String, Object> list = ();return (k,l,l1);}/*** 集合添加* @param key* @param value*/public void add(String key,Object value){SetOperations<String, Object> set = ();(key,value);}/*** 集合获取* @param key* @return*/public Set<Object> setMembers(String key){SetOperations<String, Object> set = ();return (key);}/*** 有序集合添加* @param key* @param value* @param scoure*/public void zAdd(String key,Object value,double scoure){ZSetOperations<String, Object> zset = ();(key,value,scoure);}/*** 有序集合获取* @param key* @param scoure* @param scoure1* @return*/public Set<Object> rangeByScore(String key,double scoure,double scoure1){ZSetOperations<String, Object> zset = ();return (key, scoure, scoure1);}}测试用的Controllerpackage ;import ;import ;import ;import ;import ;@RestControllerpublic class DemoController {@Autowiredprivate RedisService redisService ;@RequestMapping(value = /test)public String demoTest(){boolean result = (1,value);Object object = (1);Object name = (name);(object);return result+-------+object+ ---+name;}}使用Redis-cli客户端查看

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: SpringBoot

“资深开发者的机密战略-SpringBoot中经常使用ObjectMapper的终极指南 (资深开发者的工作内容)” 的相关文章

详解基于-的-WebSocket-运行开发-SpringBoot (基于是什么意思啊)

详解基于-的-WebSocket-运行开发-SpringBoot (基于是什么意思啊)

在现代Web运行中,实时交互和数据推送的需求日益增长。WebSocket协定作为一种全双工通讯协定,准许服务端与客户端之间建设耐久性的衔接,实事实时、双向的数据传输,极大地优化了用户体验。本文将具...

揭秘其强大功能背后的秘密-深入剖析SpringBoot底层原理 (揭秘其强大功夫的成语)

揭秘其强大功能背后的秘密-深入剖析SpringBoot底层原理 (揭秘其强大功夫的成语)

SpringBoot原理详解 简介 SpringBoot是一个开源框架,它简化了Java应用程序开发,特别是Web应用的开发。通过使用SpringBoot,开发者可以轻松创建和配置Spring应...

SpringBoot接口参数校验N种适用技巧大揭秘 (springernature)

SpringBoot接口参数校验N种适用技巧大揭秘 (springernature)

环境:SpringBoot2.6.12 实践的开发上班中大部分的接口都是须要启动参数有效性校验的,参数或者是便捷的基本数据类型,也或者是对象类型,基本上一切接纳参数的接口都是须要对这些参数启...

Boot的Java全栈项目-我在前端编写基于Spring (boot的jar包无法启动)

Boot的Java全栈项目-我在前端编写基于Spring (boot的jar包无法启动)

前言 本文将使用 NestJs + Sequelize + MySQL 完成基础运行,带大家了解 Node 服务端的基础搭建,同时也会对比 Java SpringBoot 项目的基础结构。...

SpringBoot-中的热部署和热加载 (springernature)

SpringBoot-中的热部署和热加载 (springernature)

在SpringBoot开发调试中,假设咱们每修正一行代码都须要重启调试,或许会比拟耗时。 SpringBoot团队针对这个疑问提供了spring-boot-devtools(简称:Devto...

SpringBoot-虚构线程的强强联结-接口吞吐量直线回升-效率飙升体验惊艳! (springboot)

SpringBoot-虚构线程的强强联结-接口吞吐量直线回升-效率飙升体验惊艳! (springboot)

在这篇博客中,咱们将看到如何在spring-boot中应用loom虚构线程。咱们还将在JMeter的协助下做一些负载测试,看看虚构线程和普通线程的照应期间如何。 首先,虚构线程是Projec...

深入理解其工作原理和最佳实践-SpringBoot的配置加载机制 (深入理解yii2)

深入理解其工作原理和最佳实践-SpringBoot的配置加载机制 (深入理解yii2)

概述 SpringBoot 的配置加载机制基于 Environment 的属性源管理,它提供了一种非常灵活的方式来管理应用程序的配置信息。Environment 支持多种配置格式,可以根据不同...