Java Jackson九州连结 ObjectMapper 教学与注意事项

Jackson ObjectMapper 是Java 中应用非常广泛的序列化

Java Jackson九州连结 ObjectMapper 教学与注意事项_第1张图片

酷游容容kuk32191手机v搜报导:反序列化的工具,它可以帮助我们简单、快速将Java 物件与json 之间作转换,就连Spring Framework 将它作为预设转换器。不过,一旦使用的人多,错误的写法也就层出不穷,如果没有按照正确做法,将很容易导致问题,本文将描述如何避免与改善。

问题描述

你能看出这段程式码有什么问题吗 ?

public String toJson(Something something) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.writeValueAsString(something);
}

问题在于new ObjectMapper()

这段程式码是很经典的错误,而且这种错误随处可见,很多人却没注意到。事实上,执行 new ObjectMapper() 是非常昂贵的,在系统遭遇高并发(High Concurrency)情况下,这种写法很容易出现效能瓶颈。根据这篇文章的实验,若每次序列化/反序列化都使用new ObjectMapper,比起共用一个ObjectMapper,执行时间至少相差五倍,因此要尽快修正。

解法

解法很简单,根据官方文件指出,ObjectMapper 是thread-safe,因此只要共用同一个instance,而不要每次都 new 即可,否则代价很高。我常用的作法有:

解法1. 宣告成员变数

若你的ObjectMapper 不需要任何configure,其实Spring 已经帮我们建好一个预设的,直接注入即可,当然这里还是建议使用Constructor Based Dependency Injection

public class MyService {

    private final ObjectMapper objectMapper;
    
    @Autowired
    public MyService(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    public String toJson(Something something) throws JsonProcessingException {
        return objectMapper.writeValueAsString(something);
    }
}

ObjectMapper 强大的地方在于,它有很多参数可视需求设定。这种写法可以在new 的同时一起做configure:

public class MyService {

    private static final ObjectMapper objectMapper =
         new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    public String toJson(Something something) throws JsonProcessingException {
        return objectMapper.writeValueAsString(something);
    }
}

解法2.

如果你需要全域设定,或是有多个不同设定的ObjectMapper,建议使用此方法,并且注入时要用@Qualifier,否则将会注入预设的ObjectMapper Bean。

@Configuration
public class JacksonConfiguration {

    @Bean("customObjectMapper")
    public ObjectMapper customObjectMapper() {
        return new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
    }

}

解法3. 包装成Util (推荐)

这是我最常用的作法,我在专案中通常都只有一个ObjectMapper,因此全部的class 都共用它就够了,这时可包装成Util 方便全域使用。例外处理的部分,就依各专案需求而定,没有最佳的设计,只有最适合自己的设计。

public class JsonUtil {

    private final static ObjectMapper objectMapper = 
                new ObjectMapper()
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    private JsonUtil() {

    }

    public static String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("Occur error during parsing data to json: {}", obj, e);
            return "";
        }
    }

    public static  T toObject(String json, Class objectClass) {
        try {
            return objectMapper.readValue(json, objectClass);
        } catch (IOException e) {
            log.error("Occur error during mapping json to an object: {}", json, e);
            return null;
        }
    }
}

使用起来非常方便简单。

String json = JsonUtil.toJson(something);
Something something = JsonUtil.toObject(json, Something.class);

结论

尽量不要在每次序列化/反序列化使用时都new ObjectMapper();,这样的代价是昂贵的,比起共用同一个实例,两者的效能可以相差很多倍。ObjectMapper 是thread-safe 的物件,所以本文介绍的解法概念上是一样的,就是请放心的共用同一个ObjectMapper 实例。这是很重要的,虽然简单但别小看它,也许一个小动作可以拯救你的一天。

你可能感兴趣的:(java,spring,开发语言)