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);
}