我应该将Jackson的ObjectMapper声明为静态字段吗?

我的未来我决定 提交于 2020-03-07 19:11:16

Jackson库的ObjectMapper似乎是线程安全的

这是否意味着我应该像这样将我的ObjectMapper声明为静态字段?

class Me {
    private static final ObjectMapper mapper = new ObjectMapper();
}

而不是像这样的实例级字段?

class Me {
    private final ObjectMapper mapper = new ObjectMapper();
}

#1楼

尽管就线程安全而言,声明静态ObjectMapper是安全的,但是您应该意识到,在Java中构造静态Object变量被认为是不好的做法。 有关更多详细信息,请参见为什么将静态变量视为邪恶? (如果您愿意, 我的回答

简而言之,应避免使用静态方法,因为这样会使编写简洁的单元测试变得困难。 例如,对于静态最终ObjectMapper,您无法将JSON序列化换成虚拟代码或无操作。

另外,静态的final阻止您在运行时重新配置ObjectMapper。 您可能现在没有想到这样做的原因,但是如果您将自己锁定在静态的最终模式中,那么只需拆除类加载器,就可以重新初始化它。

在使用ObjectMapper的情况下,它很好,但总的来说,这是一种不好的做法,并且使用单例模式或控制反转来管理您的长期对象没有任何优势。


#2楼

尽管ObjectMapper是线程安全的,但我强烈建议不要将其声明为静态变量,尤其是在多线程应用程序中。 甚至不是因为这是一种不好的做法,而是因为您面临着严重的死锁风险。 我是根据自己的经验讲的。 我创建了一个具有4个相同线程的应用程序,这些线程正在从Web服务获取和处理JSON数据。 根据线程转储,我的应用程序经常停在以下命令上:

Map aPage = mapper.readValue(reader, Map.class);

除此之外,性能也不佳。 当我将静态变量替换为基于实例的变量时,停滞消失了,性能提高了三倍。 也就是说,在40分钟56秒内处理了240万个JSON文档,而不是之前的2.5个小时。


#3楼

是的,这是安全的,建议您这样做。

您所引用页面的唯一警告是,共享器一旦被共享就无法修改。 但是您不更改配置,这样就可以了。 如果确实需要更改配置,则可以从静态块执行此操作,也可以。

编辑 :(2013/10)

在2.0及更高版本中,可以通过注意到还有一个更好的方法来增强上述功能:使用ObjectWriterObjectReader对象,它们可以由ObjectMapper构造。 它们是完全不变的,线程安全的,这意味着从理论上讲甚至不可能导致线程安全问题(如果代码尝试重新配置实例,则可能在ObjectMapper发生)。


#4楼

com.fasterxml.jackson.databind.type.TypeFactory._hashMapSuperInterfaceChain(HierarchicType)

com.fasterxml.jackson.databind.type.TypeFactory._findSuperInterfaceChain(Type, Class)
  com.fasterxml.jackson.databind.type.TypeFactory._findSuperTypeChain(Class, Class)
     com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(Class, Class, TypeBindings)
        com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(JavaType, Class)
           com.fasterxml.jackson.databind.type.TypeFactory._fromParamType(ParameterizedType, TypeBindings)
              com.fasterxml.jackson.databind.type.TypeFactory._constructType(Type, TypeBindings)
                 com.fasterxml.jackson.databind.type.TypeFactory.constructType(TypeReference)
                    com.fasterxml.jackson.databind.ObjectMapper.convertValue(Object, TypeReference)

com.fasterxml.jackson.databind.type.TypeFactory类中的_hashMapSuperInterfaceChain方法已同步。 在高负载下看到相同的竞争。

避免静态ObjectMapper的另一个原因


#5楼

如果您不想将其定义为静态最终变量,但想节省一些开销并确保线程安全,则可以从此PR中了解到一个技巧。

private static final ThreadLocal<ObjectMapper> om = new ThreadLocal<ObjectMapper>() {
    @Override
    protected ObjectMapper initialValue() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
};

public static ObjectMapper getObjectMapper() {
    return om.get();
}

归功于作者。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!