【问题标题】:How do I get a reference to the Jackson Object Mapper in a jersey2 / hk2 application如何在 jersey2 / hk2 应用程序中获取对 Jackson 对象映射器的引用
【发布时间】:2015-05-15 11:55:13
【问题描述】:

我有一个 jersey2 应用程序配置为通过 Jackson 支持 JSON,添加

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey.version}</version>
</dependency>

在 POM 文件中和

public MyApplication() {
    ...
    register(JacksonFeature.class)
    ...
}

在我的应用程序中。一切正常,我的资源得到反序列化 POJO 作为参数

@POST @Consumes(MediaType.APPLICATION_JSON)
public void blah(MyPojo p) {
    ...
}

现在其中一个资源需要引用 Jackson 的 ObjectMapper 以自行进行一些反序列化。我试过做类似的事情

@Inject
public MyResource(@Context ObjectMapper mapper) {
    ...
}

@GET
public String foo(@Context ObjectMapper mapper) {
    ...
}

但在这两种情况下,对mapper 的引用都是空的。如何在我的资源中注入对ObjectMapper 的引用?

【问题讨论】:

    标签: java json dependency-injection jersey-2.0 hk2


    【解决方案1】:

    首先,Jackson 提供者没有使用默认的ObjectMapper。它实际上根本不使用ObjectMapper。它利用其他 Jackson API 来处理(反)序列化。

    如果你想使用/注入单个 ObjectMapper 实例,那么你应该为它创建一个 Factory

    public class ObjectMapperFactory implements Factory<ObjectMapper> {
    
        final ObjectMapper mapper = new ObjectMapper();
    
        @Override
        public ObjectMapper provide() {
            return mapper;
        }
    
        @Override
        public void dispose(ObjectMapper t) {}   
    }
    

    然后绑定它

    register(new AbstractBinder(){
        @Override
        public void configure() {
            bindFactory(ObjectMapperFactory.class)
                .to(ObjectMapper.class).in(Singleton.class);
        }
    });
    

    需要注意的一点是ObjectMapper 的任何配置都不是线程安全的。所以说你试图从你的资源方法配置它,这些操作不是线程安全的。

    Jackson 提供者需要注意的另一件事是,如果我们提供ContextResolver,例如mentioned by @Laurentiu L,那么Jackson 提供者将切换到使用我们的ObjectMapper。在这种情况下,如果您想使用相同的ObjectMapper,那么您可以在Factory 中查找它。例如

    public class ObjectMapperFactory implements Factory<ObjectMapper> {
    
        private final Providers providers;
        final ObjectMapper mapper = new ObjectMapper();
    
        public ObjectMapperFactory(@Context Providers providers) {
            this.providers = providers;
        }
    
        @Override
        public ObjectMapper provide() {
            ContextResolver<ObjectMapper> resolver = providers.getContextResolver(
                    ObjectMapper.class, MediaType.APPLICATION_JSON);
            if (resolver == null) { return mapper; }
    
            return resolver.getContext(null);
        }
    
        @Override
        public void dispose(ObjectMapper t) {}   
    }
    

    为使上述工作(使用单个ObjectMapper),您需要确保实现ContextResolver&lt;ObjectMapper&gt;,并确保使用相应的@Produces@Consumes 媒体类型注释ContextResolver .

    【讨论】:

    • 根据杰克逊文档,只要不进行重新配置,ObjectMapper 实例就是线程安全的。 “映射器实例是完全线程安全的,前提是实例的所有配置都发生在任何读取或写入调用之前。” fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/…
    • @RicardNàcherRoig 这不是我说的吗? “需要注意的是,ObjectMapper 的任何配置都不是线程安全的”。如果措辞不正确,那就是我的意思。您不应该尝试在运行时重新配置映射器;同一个映射器可能正在用于另一个请求。
    • 感谢@paul-samsotha 的回复。在我看来,它的措辞不正确,或者至少我认为 Jackson API 文档中的解释要清楚得多。我浪费了一些时间认为 ObjectMapper 从来都不是线程安全的。
    【解决方案2】:

    除了 JacksonFeature,您还需要为 ObjectMapper 注册一个 ContextResolver。

    来自 9.1.4.2 的 Documentation 的简单示例。配置和注册

    @Provider
    public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
    
        final ObjectMapper defaultObjectMapper;
    
        public MyObjectMapperProvider() {
            defaultObjectMapper = createDefaultMapper();
        }
    
        @Override
        public ObjectMapper getContext(Class<?> type) {
            return defaultObjectMapper;
        }
    
        private static ObjectMapper createDefaultMapper() {
            final ObjectMapper result = new ObjectMapper();
            result.configure(Feature.INDENT_OUTPUT, true);
    
            return result;
        }
    
        // ...
    }
    

    完整的代码示例 available on Github

    您还需要注册它

            .register(MyObjectMapperProvider.class)  
    

    【讨论】:

    • 这行得通,但由于我不需要(还没有?)自定义提供的ObjectMapper 我希望有一种方法来使用泽西创建的而不是提供我自己的。
    • 如果有人在使用 Dropwizard 时遇到“问题:抽象类型要么需要映射到具体类型”,即使您使用 addAbstractTypeMapping 注册了一个模块,这个解决方案就像一个魅力一样解决了这个问题(只需注册你在createDefaultMapper 中的模块,并在Applicationrun() 中使用environment.jersey().register() 注册这个Provider
    猜你喜欢
    • 1970-01-01
    • 2018-12-07
    • 2014-09-18
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多