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

Should I declare Jackson's ObjectMapper as a static field?

杰克逊图书馆的ObjectMapper类似乎是线程安全的。

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

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

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

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

是的,这是安全的,推荐的。

您所引用的页面中唯一的警告是,一旦映射器被共享,您就不能修改它的配置;但是您没有更改配置,所以这是可以的。如果您确实需要更改配置,那么您可以从静态块中进行更改,这样也可以。

编辑:(2013/10)

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


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

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

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


虽然在线程安全方面声明静态对象映射器是安全的,但是您应该意识到在Java中构造静态对象变量被认为是不好的实践。有关更多详细信息,请参阅为什么静态变量被认为是有害的?(如果你愿意,我的回答)

简而言之,应该避免使用静态,因为这样会使编写简洁的单元测试变得困难。例如,使用静态最终对象映射器,不能将JSON序列化换成伪代码或no-op。

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

在对象映射器的情况下,这是很好的,但一般来说这是不好的实践,并且没有优势比使用单例模式或控制反转来管理长寿的对象。


如果您不想将其定义为静态最终变量,但希望节省一些开销并保证线程安全,那么我从这个pr中学到了一个技巧。

1
2
3
4
5
6
7
8
9
10
11
12
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();
}

归功于作者。


com.fasterxml.jackson.databind.type.typefactory.u hashmapsuperinterfacechain(hierarchictype)

1
2
3
4
5
6
7
8
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已同步。在高负载情况下,我看到了相同的竞争。

可能是避免使用静态对象映射器的另一个原因