11

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

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

我已经按照这里的各种问题来了解如何自定义 ObjectMapper。特别是,我声明了一个 ContextResolver,标记为 @javax.ws.rs.ext.Provider 和 @javax.inject.Singleton。

我有一个 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;
}

映射器定义

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

但是 - 仅使用此配置,永远不会调用 getContext(),因此永远不会注册映射器。我陷入了一个典型的谜团——注释——谜团,实际上无法追踪到我实际上应该做的事情。Spring 用户只需报告注册组件,容器就可以获取它。

这个答案谈到覆盖我自己的 javax.ws.rs.core.Application 实现。但是,这在 GuiceContainer 的 jersey-guice 实现中看起来是硬连线的,即 DefaultResourceConfig():

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

我应该在这里继承 GuiceContainer 吗?还是我缺少其他一些魔术注释?

这似乎是一件相当普遍的事情——我很惊讶用这种 guice 组合来证明它是多么困难。

4

1 回答 1

24

我陷入了一个典型的谜团——注释——谜团,实际上无法追踪到我实际上应该做的事情。Spring 用户只需报告注册组件,容器就可以获取它。

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

例如,你不需要ContextResolver,你应该使用普通的 GuiceProvider代替:

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

然后你应该为你的模块添加相应的绑定:

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

这将绑定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);
    }
}

然后绑定它:

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

这就是您需要做的所有事情 - 不需要子类化。

虽然有代码大小优化的空间。如果我是你,我会使用@Provides-methods:

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

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

于 2013-05-26T10:46:41.233 回答