ObjectMapper在项目中的正确用法

这是DDD&微服务系列的第18篇,欢迎持续关注~

1. 每次new一个

在SpringBoot项目中要实现对象与Json字符串的互转,每次都需要像如下一样new 一个ObjectMapper对象:

java 复制代码
public UserEntity string2Obj(String json) throws JsonProcessingException {
	ObjectMapper objectMapper = new ObjectMapper();
	return objectMapper.readValue(json, UserEntity.class);
}

public String obj2String(UserEntity userEntity) throws JsonProcessingException {
	ObjectMapper objectMapper = new ObjectMapper();
	return objectMapper.writeValueAsString(car)
}

这样的代码到处可见,有问题吗?

你要说他有问题吧,确实能正常执行;可你要说没问题吧,在追求性能的同学眼里,这属实算是十恶不赦的代码了。

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

基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。 而JMH是一个用来构建,运行,分析Java或其他运行在JVM之上的语言的 纳秒/微秒/毫秒/宏观 级别基准测试的工具。

JAVA 复制代码
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 3, time = 1)
public class JsonJMHTest {
    
    String json = "{\"id\":122345667,\"email\":\"jianzh5@163.com\",\"price\":12.25}";
    UserEntity userEntity = new UserEntity(13345L,"jianzh5@163.com", BigDecimal.valueOf(12.25));
    /**
     * 测试String to Object
     */
    @Benchmark
    public UserEntity objectMapper2ObjTest() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(json, UserEntity.class);
    }

    /**
     * 测试Object to String
     */
    @Benchmark
    public String objectMapper2StringTest() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.writeValueAsString(userEntity);
    }
  
  	public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JsonJMHTest.class.getSimpleName())
                .build();
        new Runner(opt).run();
    }
}

测试环境

bash 复制代码
# JMH version: 1.36
# VM version: JDK 17.0.3, OpenJDK 64-Bit Server VM, 17.0.3+7-LTS
# Mac AppleM1/16GB

测试结果

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

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

2. 单例化

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

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

java 复制代码
@Getter
public enum ObjectMapperInstance {
    
    INSTANCE;
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    ObjectMapperInstance() {
        
    }
}

再次使用JMH对其测试:

java 复制代码
@Benchmark
public UserEntity singleten2ObjTest() throws JsonProcessingException {
  ObjectMapper objectMapper = ObjectMapperInstance.INSTANCE.getObjectMapper();
  return objectMapper.readValue(json, UserEntity.class);
}

@Benchmark
public String singleten2StringTest() throws JsonProcessingException {
  ObjectMapper objectMapper = ObjectMapperInstance.INSTANCE.getObjectMapper();
  return objectMapper.writeValueAsString(userEntity);
}

测试结果如下:

可以看到,使用单例模式,String转对象的方法每秒可以执行420多万次,比new ObjectMapper的方式快了18倍;而对象转String的方法每秒可以执行830万次,性能提升了300倍(看到结果的一瞬间我傻眼了,一度怀疑是写错代码了)!!!!

3. 个性化配置

当然,在项目中使用ObjectMapper时,有时候我们还需要做一些个性化配置,比如将Long和BigDemical类型的属性都通过字符串格式进行转换,防止前端使用时丢失数值精度。

这些类型转换的格式映射都可以在单例类中配置,代码如下:

JAVA 复制代码
@Getter
public enum ObjectMapperInstance {
    
    INSTANCE;
    
    private final ObjectMapper objectMapper;
    
    ObjectMapperInstance() {
        objectMapper = new ObjectMapper();
        // 注册自定义模块
        initialize();
    }
    
    private void initialize() {
        CustomJsonModule customJsonModule = new CustomJsonModule();
        objectMapper.registerModule(customJsonModule);
    }
}

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

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

再来一次JMH测试:

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

4. 小结

通过上面的测试,结论已经很清晰了。使用单例模式进行字符串转对象时性能可以提升18倍,而对象转String性能快了惊人的300倍,所以在Spring中如何正确的使用ObjectMapper不用我再说了吧~

DailyMart是一个基于领域驱动设计(DDD)和Spring Cloud Alibaba的微服务商城系统。我们将在该系统中整合博主其他专栏文章的核心内容。如果你对这两大技术栈感兴趣,可以在公众号 JAVA日知录 回复关键词 DDD 以获取相关源码。

相关推荐
fanly119 小时前
surging 发布命令行工具
微服务·surging
悟空码字15 小时前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5512 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602735 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840825 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解6 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解6 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记6 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者6 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840826 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp