我似乎无法正确注册我的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;
}
Mapper定义
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用户只报告注册组件,而容器只是选择它。
This回答谈论覆盖我自己的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组合有多么困难。
答案 0 :(得分:24)
我陷入了典型的诡计 - 注释 - 神秘,它在哪里 实际上无法实现我实际应该做的事情。 Spring用户只报告注册组件和容器 只是把它拿起来。
你真的应该阅读excellent Guice documentation。 Guice非常易于使用,它具有非常少量的基本概念。你的问题在于你混合了Jersey JAX-RS依赖注入和Guice依赖注入。如果您正在使用GuiceContainer
,那么您声明将使用Guice 用于DI的所有,因此您必须使用Guice而不是JAX-RS添加绑定。
例如,您不需要ContextResolver
,您应该使用简单的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;
}
}
然后你应该为你的模块添加相应的绑定:
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(Singleton.class);
这将绑定ObjectMapper
,但仅使用泽西与杰克逊是不够的。您需要某种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
- 方法:
@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
而不是普通ServletModule
,它为你提供了很多有用的绑定。