Java ObjectMapper-线程安全和性能的最佳实践 总结

Java ObjectMapper-线程安全和性能的最佳实践 总结,java,json,performance,thread-safety,jackson-databind,Java,Json,Performance,Thread Safety,Jackson Databind,我想在下面描述的用例上下文中找到使用ObjectMapper和/或ObjectReader在线程安全和性能方面的最佳实践 背景 我有一个助手类(Json.java),其中方法toObject()使用ObjectMapper将Json字符串转换为给定(Json可映射)类的对象 问题 我已经读过ObjectReader经常被推荐为完全线程安全的,但我主要看到它是在一个非泛型上下文中,其中要读取的类是预定义的。在这种情况下,您认为线程安全性和性能方面的最佳实践是什么?在代码中,我有三条建议可以作为起点

我想在下面描述的用例上下文中找到使用
ObjectMapper
和/或
ObjectReader
在线程安全和性能方面的最佳实践

背景 我有一个助手类(Json.java),其中方法
toObject()
使用
ObjectMapper
Json
字符串转换为给定(Json可映射)类的对象

问题 我已经读过
ObjectReader
经常被推荐为完全线程安全的,但我主要看到它是在一个非泛型上下文中,其中要读取的类是预定义的。在这种情况下,您认为线程安全性和性能方面的最佳实践是什么?在代码中,我有三条建议可以作为起点

我曾尝试浏览过jackson databind的源代码和文档,但我的Java理论技能还不足以从中得出答案。我也在某某和其他地方研究过类似的问题,但没有发现任何与我的情况足够接近的问题

import java.io.IOException;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入com.fasterxml.jackson.databind.ObjectReader;
公共抽象类Json{
私有静态最终ObjectMapper jsonMapper=newObjectMapper();
//注:jsonReader仅与建议3相关。
私有静态最终ObjectReader jsonReader=jsonMapper.reader();
//建议1:
公共静态T ToObject 1(最终类类型,最终字符串json)引发IOException{
返回jsonMapper.readValue(json,类型);
}
//建议2:
公共静态T ToObject 2(最终类类型,最终字符串json)引发IOException{
返回jsonMapper.readerFor(type).readValue(json);
}
//建议3:
公共静态T ToObject 3(最终类类型,最终字符串json)引发IOException{
返回jsonReader.forType(type).readValue(json);
}
//为了简洁起见,省略了类的其余部分。
}

正如我在评论中提到的,我一直使用建议1。我不知道这两个选项在线程安全性/性能方面是否存在差异

但是,如果目标类型本身使用泛型类型参数化,则此方法将不起作用。最明显的用法是一些集合:

Json.toObject1(List.class, str);  // will deserialize into List<Object>

关于并发性

ObjectMapper
ObjectReader
在这里不相关。
ObjectReader
看起来对您的场景没有帮助。
其规格说明如下:

可用于的每个序列化配置的生成器对象 反序列化参数,例如要使用的根类型或要使用的对象 更新(而不是构造新实例)

请注意,
ObjectMapper
ObjectReader
的两个实例都是线程安全的,前提是它们的配置在序列化/反序列化客户端调用之间没有更改。
ObjectReader
确实指定了:

如果所有配置 在任何读或写调用之前发生实例的

ObjectReader
的不同之处在于,更新其配置将使其返回文档中所述的新实例的方式是不可变的:

使用“突变工厂”模式,使实例不可变(和 因此完全线程安全,无需外部同步);新的 实例是为不同的配置构造的

在您的需求中,您不希望在客户端调用之间更改配置。因此,使用
ObjectMapper
看起来更相关。
因此,我将消除3)方法和2)方法,因为(type)的
jsonMapper.reader是
ObjectReader
实例的工厂方法。不过,在这里使用
ObjectReader
并不重要

因此,最简单和常见的方法看起来更好:

// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
 }
//建议1:
公共静态T ToObject 1(最终类类型,最终字符串json)引发IOException{
返回jsonMapper.readValue(json,类型);
}
关于性能的信息

此外,请记住,
ObjectReader
是不可变的。因此,2和3种方法在每次调用时创建
ObjectReader
的新实例。 这看起来不是一个好的性能提示。
是的,这些是轻量级对象,但每次创建它们都要付出代价。
ObjectReader
doc说:

实例最初由ObjectMapper构建,可以重用, 共享、缓存;既因为线程安全,又因为实例 它们的重量相对较轻

在这里,您不会重用这些实例。因此,您将失去缓存和性能方面的任何好处。
您可以将它们存储到
Map
字段中并重用它们,但只有在您需要提高
ObjectMapper
的实际性能时,才可以这样做,当然,在结束任何操作之前都要进行测量

结论:对于您的用例,我认为第一个解决方案(
ObjectMapper
)的性能和并发性更好

构建一个
ObjectMapper
实例是一个相对昂贵的操作,因此建议创建一个对象并重用它。你做对了,使它成为最终版本

// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
}
没有区别,真的。这两种方法都将构造一个新的
ObjectReader
对象:前者(
jsonMapper.reader for(type)
)将直接为您提供一个完全构建的实例,后者(
jsonReader.forType(type)
)将补充尚未使用的
jsonReader
)并返回一个随时可用的对象。我宁愿选择第二个选项,因为我不想保留那个字段

你不应该担心性能
private static final ObjectMapper jsonMapper = new ObjectMapper();
// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
}
// Suggestion 2:
public static <T> T toObject2(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readerFor(type).readValue(json);
}

// Suggestion 3:
public static <T> T toObject3(final Class<T> type, final String json) throws IOException {
    return jsonReader.forType(type).readValue(json);
}