如何用Guice / Jersey钩住Jackson ObjectMapper

时间:2021-12-07 18:05:47

I can't seem to get my Jackson ObjectMapper Module registered correctly.

我似乎无法正确地注册我的Jackson ObjectMapper模块。

I'm using a Guice + Jersey + Jackson (FasterXML) stack.

我正在使用Guice + Jersey + Jackson (FasterXML)堆栈。

I've followed how to customise the ObjectMapper based on various question here. In particular, I have a ContextResolver declared, marked as an @javax.ws.rs.ext.Provider and a @javax.inject.Singleton.

我一直在关注如何根据这里的各种问题定制ObjectMapper。特别地,我声明了一个上下文解析器,标记为@javax.ws.r .ext。提供者和@javax.inject.Singleton。

I have a GuiceServletContextListener along the lines of:

我有一个GuiceServletContextListener,写着:

@Override
protected Injector getInjector() {

     Injector injector = Guice.createInjector(new DBModule(dataSource),
            new ServletModule()
            {
                @Override
                protected void configureServlets() {


                    // Mapper
                    bind(JacksonOMP.class).asEagerSingleton();

                    // ... 

                    Map<String, String> initParams = new HashMap<String, String>();
                    initParams.put("com.sun.jersey.config.feature.Trace",
                            "true");
                    initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");

                    serve("/services/*").with(
                            GuiceContainer.class,
                            initParams);
                }
            });

    return injector;
}

Mapper defined

映射器定义

import javax.inject.Singleton;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
@Singleton
@Produces
public class JacksonOMP implements ContextResolver<ObjectMapper> {

  @Override
  public ObjectMapper getContext(Class<?> aClass) {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Hibernate4Module());
    return mapper;
  }
}

However - with this configuration alone, getContext() is never called, so the mapper is never registered. I'm stuck in a typical guice--annotations-mystery, where it's practically untraceable to what I'm actually supposed to be doing. Spring users just report registering the component, and the container just picks it up.

然而—仅使用这个配置,getContext()就不会被调用,因此映射器也不会被注册。我被困在一个典型的谜题中——注释——神秘,实际上它无法追溯我实际上应该做什么。Spring用户只报告注册组件,容器只会接收它。

This answer talks about overriding my own javax.ws.rs.core.Application implementation. However, this looks hard-wired in the jersey-guice impementation of GuiceContainer to be DefaultResourceConfig():

这个答案涉及到覆盖我自己的javax.ws.r .core。应用程序实现。然而,这在GuiceContainer的jersy -guice嵌套中看起来是硬连线的,它是DefaultResourceConfig():

@Override
protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props,
        WebConfig webConfig) throws ServletException {
    return new DefaultResourceConfig();
}

Am I supposed subclass GuiceContainer here? Or is there some other magic annotation that I'm missing?

我应该是GuiceContainer的子类吗?或者我还遗漏了什么其他神奇的注释?

This seems a fairly common thing to want to do - I'm surprised at how hard it's proving to do with this guice combination.

这似乎是一件很普通的事情——我很惊讶于它与guice的组合有多么困难。

1 个解决方案

#1


23  

I'm stuck in a typical guice--annotations-mystery, where it's practically untraceable to what I'm actually supposed to be doing. Spring users just report registering the component, and the container just picks it up.

我被困在一个典型的谜题中——注释——神秘,实际上它无法追溯我实际上应该做什么。Spring用户只报告注册组件,容器只会接收它。

You really should read excellent Guice documentation. Guice is very easy to use, it has very small number of basic concepts. Your problem is in that you mixed Jersey JAX-RS dependency injection and Guice dependency injection. If you are using GuiceContainer then you declare that you will be using Guice for all of your DI, so you have to add bindings with Guice and not with JAX-RS.

您确实应该阅读优秀的Guice文档。Guice很容易使用,它有非常少的基本概念。您的问题是您混合了Jersey JAX-RS依赖注入和Guice依赖注入。如果您正在使用GuiceContainer,那么您将声明您将对所有DI使用Guice,因此必须添加与Guice的绑定,而不是与JAX-RS的绑定。

For instance, you do not need ContextResolver, you should use plain Guice Provider instead:

例如,您不需要上下文解析器,您应该使用普通的Guice Provider:

import com.google.inject.Provider;

public class ObjectMapperProvider implements Provider<ObjectMapper> {
    @Override
    public ObjectMapper get() {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate4Module());
        return mapper;
    }
}

Then you should add corresponding binding to your module:

然后在模块中添加相应的绑定:

bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(Singleton.class);

This will bind ObjectMapper, but it is not enough to use Jersey with Jackson. You will need some kind of MessageBodyReader/MessageBodyWriter, e.g. JacksonJsonProvider. You will need another provider for it:

这将绑定ObjectMapper,但仅将Jersey与Jackson结合使用是不够的。您需要某种MessageBodyReader/MessageBodyWriter,例如JacksonJsonProvider。您将需要另一个提供者:

public class JacksonJsonProviderProvider implements Provider<JacksonJsonProvider> {
    private final ObjectMapper mapper;

    @Inject
    JacksonJsonProviderProvider(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public JacksonJsonProvider get() {
        return new JacksonJsonProvider(mapper);
    }
}

Then bind it:

然后绑定:

bind(JacksonJsonProvider.class).toProvider(JacksonJsonProviderProvider.class).in(Singleton.class);

This is all you need to do - no subclassing is needed.

这就是您需要做的——不需要子类化。

There is a room for code size optimization though. If I were you I would use @Provides-methods:

尽管如此,还是有空间进行代码大小优化。如果我是你,我会使用@ provides方法:

@Provides @Singleton
ObjectMapper objectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Hibernate4Module());
    return mapper;
}

@Provides @Singleton
JacksonJsonProvider jacksonJsonProvider(ObjectMapper mapper) {
    return new JacksonJsonProvider(mapper);
}

These methods should be added to one of your modules, e.g. to anonymous ServletModule. Then you won't need separate provider classes.
BTW, you should use JerseyServletModule instead of plain ServletModule, it provides a lot of useful bindings for you.

这些方法应该添加到您的一个模块中,例如,添加到匿名ServletModule。那么您就不需要单独的提供程序类了。顺便说一句,您应该使用JerseyServletModule而不是普通的ServletModule,它为您提供了许多有用的绑定。

#1


23  

I'm stuck in a typical guice--annotations-mystery, where it's practically untraceable to what I'm actually supposed to be doing. Spring users just report registering the component, and the container just picks it up.

我被困在一个典型的谜题中——注释——神秘,实际上它无法追溯我实际上应该做什么。Spring用户只报告注册组件,容器只会接收它。

You really should read excellent Guice documentation. Guice is very easy to use, it has very small number of basic concepts. Your problem is in that you mixed Jersey JAX-RS dependency injection and Guice dependency injection. If you are using GuiceContainer then you declare that you will be using Guice for all of your DI, so you have to add bindings with Guice and not with JAX-RS.

您确实应该阅读优秀的Guice文档。Guice很容易使用,它有非常少的基本概念。您的问题是您混合了Jersey JAX-RS依赖注入和Guice依赖注入。如果您正在使用GuiceContainer,那么您将声明您将对所有DI使用Guice,因此必须添加与Guice的绑定,而不是与JAX-RS的绑定。

For instance, you do not need ContextResolver, you should use plain Guice Provider instead:

例如,您不需要上下文解析器,您应该使用普通的Guice Provider:

import com.google.inject.Provider;

public class ObjectMapperProvider implements Provider<ObjectMapper> {
    @Override
    public ObjectMapper get() {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate4Module());
        return mapper;
    }
}

Then you should add corresponding binding to your module:

然后在模块中添加相应的绑定:

bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(Singleton.class);

This will bind ObjectMapper, but it is not enough to use Jersey with Jackson. You will need some kind of MessageBodyReader/MessageBodyWriter, e.g. JacksonJsonProvider. You will need another provider for it:

这将绑定ObjectMapper,但仅将Jersey与Jackson结合使用是不够的。您需要某种MessageBodyReader/MessageBodyWriter,例如JacksonJsonProvider。您将需要另一个提供者:

public class JacksonJsonProviderProvider implements Provider<JacksonJsonProvider> {
    private final ObjectMapper mapper;

    @Inject
    JacksonJsonProviderProvider(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public JacksonJsonProvider get() {
        return new JacksonJsonProvider(mapper);
    }
}

Then bind it:

然后绑定:

bind(JacksonJsonProvider.class).toProvider(JacksonJsonProviderProvider.class).in(Singleton.class);

This is all you need to do - no subclassing is needed.

这就是您需要做的——不需要子类化。

There is a room for code size optimization though. If I were you I would use @Provides-methods:

尽管如此,还是有空间进行代码大小优化。如果我是你,我会使用@ provides方法:

@Provides @Singleton
ObjectMapper objectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Hibernate4Module());
    return mapper;
}

@Provides @Singleton
JacksonJsonProvider jacksonJsonProvider(ObjectMapper mapper) {
    return new JacksonJsonProvider(mapper);
}

These methods should be added to one of your modules, e.g. to anonymous ServletModule. Then you won't need separate provider classes.
BTW, you should use JerseyServletModule instead of plain ServletModule, it provides a lot of useful bindings for you.

这些方法应该添加到您的一个模块中,例如,添加到匿名ServletModule。那么您就不需要单独的提供程序类了。顺便说一句,您应该使用JerseyServletModule而不是普通的ServletModule,它为您提供了许多有用的绑定。