Jackson ObjectMapper 是Java 中应用非常广泛的序列化
酷游容容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
即可,否则代价很高。我常用的作法有:
若你的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);
}
}
如果你需要全域设定,或是有多个不同设定的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);
}
}
这是我最常用的作法,我在专案中通常都只有一个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 实例。这是很重要的,虽然简单但别小看它,也许一个小动作可以拯救你的一天。