我是否应该将Jackson's ObjectMapper声明为静态字段?

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

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

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

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

class Me {
private final ObjectMapper mapper = new ObjectMapper();
}
201912 次浏览

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

您所提到的页面中唯一需要注意的是,一旦共享了映射器,您就不能修改它的配置;但是你没有改变构型,所以这很好。如果你确实需要改变配置,你可以从静态块来做,这也会很好。

编辑: (2013/10)

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

尽管从线程安全的角度来说,声明静态ObjectMapper是安全的,但您应该意识到,在Java中构造静态Object变量被认为是一种糟糕的做法。有关更多详细信息,请参见为什么静态变量被认为是邪恶的?(如果你愿意,还可以查看我的答案)。

简而言之,应该避免静态,因为它使编写简洁的单元测试变得困难。例如,使用静态最终ObjectMapper,您不能将JSON序列化替换为虚拟代码或无操作。

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

在ObjectMapper的情况下,它很好,但一般来说,这是一种糟糕的实践,使用单例模式或反转控制来管理长生命期对象并没有什么优势。

虽然ObjectMapper是线程安全的,但我强烈反对将其声明为静态变量,特别是在多线程应用程序中。 甚至不是因为这是一种糟糕的实践,而是因为您面临着严重的死锁风险。我是根据自己的经验讲的。我创建了一个具有4个相同线程的应用程序,用于从web服务获取和处理JSON数据。 根据线程转储,我的应用程序经常在执行以下命令时暂停
Map aPage = mapper.readValue(reader, Map.class);

除此之外,表现并不好。 当我用基于实例的变量替换静态变量时,失速消失了,性能翻了两番。也就是说,240万个JSON文档在40分56秒内被处理。

.

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的另一个原因

这是我从公关中学到的一个技巧,如果你不想将它定义为静态final变量,但又想节省一点开销并保证线程安全。

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

归功于作者。

这个问题可能很老了,但我是这么做的。

ObjectMapper实例保存在线程安全的单例中:

public final class JacksonObjectMapperHolder {


private static volatile JacksonObjectMapperHolder INSTANCE;


private static final Object MUTEX = new Object();


public static JacksonObjectMapperHolder getInstance() {
JacksonObjectMapperHolder instance = INSTANCE;


if(instance == null) {
synchronized(MUTEX) {
instance = INSTANCE;


if(instance == null) {
INSTANCE = instance = new JacksonObjectMapperHolder();
}
}
}


return instance;
}


private final ObjectMapper objectMapper = new ObjectMapper();


private JacksonObjectMapperHolder() {
super();
}


public final ObjectMapper getObjectMapper() {
return objectMapper;
}


}