Java 我应该申报Jackson';是否将ObjectMapper作为静态字段?

Java 我应该申报Jackson';是否将ObjectMapper作为静态字段?,java,json,jackson,Java,Json,Jackson,Jackson库的ObjectMapper类 这是否意味着我应该像这样将我的ObjectMapper声明为一个静态字段 class Me { private static final ObjectMapper mapper = new ObjectMapper(); } 而不是像这样作为实例级字段 class Me { private final ObjectMapper mapper = new ObjectMapper(); } 是的,这是安全的建议 你提到的页面中唯一的

Jackson库的
ObjectMapper

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

class Me {
    private static final ObjectMapper mapper = new ObjectMapper();
}
而不是像这样作为实例级字段

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

是的,这是安全的建议

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

编辑:(2013/10)

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

尽管就线程安全而言声明静态ObjectMapper是安全的,您应该知道,在Java中构造静态对象变量被认为是不好的做法。有关更多详细信息,请参阅(如果您愿意,)

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

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


对于ObjectMapper来说,这很好,但一般来说,这是一种不好的做法,使用单例模式或控制反转来管理长期存在的对象没有任何优势。

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

Map aPage = mapper.readValue(reader, Map.class);
除此之外,表现也不好。 当我用基于实例的变量替换静态变量时,停滞消失了,性能提高了四倍。也就是说,240万个JSON文档的处理时间为40分钟56秒,而不是之前的2.5小时。

com.fasterxml.jackson.databind.type.TypeFactory.\u hashMapSuperInterfaceChain(HierarchycType)

com.fasterxml.jackson.databind.type.TypeFactory中的hashMapSuperInterfaceChain方法已同步。 在高负载时,我在同一台计算机上看到了争用


这可能是避免使用静态ObjectMapper的另一个原因,如果您不想将其定义为静态最终变量,而是想节省一些开销并保证线程安全,那么这就是我从中学到的技巧

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();
}
private static final ThreadLocal om=new ThreadLocal(){
@凌驾
受保护的ObjectMapper初始值(){
ObjectMapper ObjectMapper=新的ObjectMapper();
objectMapper.configure(在未知属性上反序列化feature.FAIL,false);
返回对象映射器;
}
};
公共静态对象映射器getObjectMapper(){
返回om.get();
}

作者的功劳。

@StaxMan:我有点担心调用后
ObjectMapper
是否仍然是线程安全的。众所周知,
ObjectMapper
只有在每次
writeValue()
之前进行克隆(例如,
SerializationConfig
),才能使用它(我怀疑)。你能揭穿我的恐惧吗?
DateFormat
确实是在幕后克隆的。很好的怀疑,但你有保险在大型企业应用程序的单元/集成测试中,我遇到过奇怪的行为。当将ObjectMapper作为静态最终类属性时,我开始面临PermGen问题。有人愿意解释可能的原因吗?我使用的是jackson databind 2.4.1版。@MiklosKrivan你看过
ObjectMapper
吗?!方法名为
writer()
reader()
(以及一些
readerFor()
writerFor()
)。没有
mapper.with()
调用(因为Jackson中的“with”意味着构造新实例和线程安全执行)。但是关于配置更改:没有进行任何检查,因此必须保护对
ObjectMapper
的配置访问。至于“copy()”:是的,这会创建一个新的副本,可以根据相同的规则完全(重新)配置它:首先完全配置它,然后使用它,这很好。相关的成本不小(因为copy不能使用任何缓存处理程序),但这是安全的方法,是的。我建议,尽管静态有状态单例通常是一个危险的标志,但有足够的理由说明在这种情况下共享单个(或少量)实例是有意义的。人们可能希望使用依赖注入来实现这一点;但与此同时,值得一问的是,是否存在需要解决的实际问题或潜在问题。这尤其适用于测试:仅仅因为某些东西在某些情况下可能有问题并不意味着它适合您的使用。所以:意识到问题,很好。假设“一刀切”,就不太好了。显然,理解任何设计决策所涉及的问题都很重要,如果你能在不给你的用例带来问题的情况下做一些事情,根据定义,你就不会造成任何问题。然而,我认为使用静态实例并没有任何好处,而且随着代码的发展或交付给其他开发人员,它为将来的重大问题打开了大门
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();
}