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

新手上路,请多包涵

概括

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

背景

我有一个帮助程序类 ( Json.java ),其中方法 toObject() 使用 ObjectMapperjson 对象转换为给定的 json-mapp- 字符串) 班级。

问题/问题

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

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

 import java.io.IOException;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;

public abstract class Json {

    private static final ObjectMapper jsonMapper = new ObjectMapper();

    // NOTE: jsonReader is only relevant for Suggestion 3.
    private static final ObjectReader jsonReader = jsonMapper.reader();

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

    // Remainder of class omitted for brevity.
}

原文由 axeleration 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.6k
2 个回答
private static final ObjectMapper jsonMapper = new ObjectMapper();

构造一个 ObjectMapper 实例是一个比较昂贵的操作,所以建议创建一个对象并重用它。你做对了 final

 // Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
}

您总是将 JSON 读取 为 POJO,所以让我们准确明了,并使用 ObjectReader

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

没有区别,真的。这两种方法都将构造一个新的 ObjectReader 对象:前者 ( jsonMapper.readerFor(type) ) 将直接为您提供一个完整的实例,后者 ( jsonReader.forType(type) -e40c2d62d69be217-e40c2d62d69be217-e40c2 not-yet-usable jsonReader 并返回一个随时可用的对象。我宁愿选择选项 2,因为我不想保留该字段。

您不必担心性能或线程安全。尽管创建 ObjectMapper 可能成本高昂(或从中复制),但获取和使用 ObjectReader s 是轻量级且完全线程安全的。

来自 Java 文档(强调我的):

使用“突变工厂”模式,使实例 不可变(因此 完全线程安全,无需外部同步);新实例是为不同的配置构建的。实例最初由 ObjectMapper 构建,可以重用、共享、缓存;既因为线程安全,也因为实例 相对轻量级

我自己最近有这些问题,并决定将 ObjectMapper#reader(InjectableValues) 作为工厂方法。这非常方便,特别是当您想要稍微自定义 ObjectReader 或像我的情况一样调整 DeserializationContext

顺便说一句,这是一个很好的问题。

原文由 Andrew Tobilko 发布,翻译遵循 CC BY-SA 4.0 许可协议

关于并发

ObjectMapperObjectReader 在这里不相关。

ObjectReader 看起来对您的场景没有帮助。

它的规范说:

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

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

ObjectReader 确实指定了:

映射器实例是完全线程安全的,前提是实例的所有配置发生在任何读取或写入调用之前。

虽然 ObjectReader 的区别在于更新其配置将使其返回新实例的方式是不可变的,如其文档所述:

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

在您的要求中,您不想更改客户端调用之间的配置。所以使用 ObjectMapper 看起来更相关。

所以我会消除 3) 方式和 2) 方式,因为 jsonMapper.readerFor(type) 这是 ObjectReader 实例的工厂方法。在这里使用 ObjectReader 仍然没有关系。

所以最简单和常见的方法看起来更好:

 // Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
 }

关于性能

此外,请记住 ObjectReader 是不可变的。所以 2 和 3 方法在每次调用时创建 ObjectReader 的新实例。它看起来不是一个很好的性能提示。

是的,这些是轻量级对象,但每次创建它们都是有成本的。

ObjectReader 文档说:

实例最初由 ObjectMapper 构建,可以重复使用、共享、缓存;既因为线程安全,也因为实例相对轻量级。

在那里你不会重用这些实例。所以你在缓存和性能方面失去了任何好处。

您可以将它们存储到 Map 字段中并重复使用它们,但仅当您需要提高 ObjectMapper 的实际性能时才这样做,当然在得出任何结论之前进行测量。

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

原文由 davidxxx 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题