问题描述
我似乎无法正确注册我的Jackson ObjectMapper模块.
I can't seem to get my Jackson ObjectMapper Module registered correctly.
我正在使用Guice + Jersey + Jackson(FasterXML)堆栈.
I'm using a Guice + Jersey + Jackson (FasterXML) stack.
我遵循了如何根据此处的各种问题来自定义ObjectMapper的方法.特别是,我声明了一个ContextResolver,标记为@ javax.ws.rs.ext.Provider和@ javax.inject.Singleton.
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.
我有一个GuiceServletContextListener,类似于:
I have a GuiceServletContextListener along the lines of:
@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用户只是报告说已注册组件,而容器只是将其拾取.
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.
此答案讨论了重写我自己的javax.ws.rs.core.Application实现.但是,在GuiceContainer的jersey-guice强制实施中,这看起来像是硬连接为DefaultResourceConfig():
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():
@Override
protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props,
WebConfig webConfig) throws ServletException {
return new DefaultResourceConfig();
}
我应该在这里子类GuiceContainer吗?还是我缺少其他魔术注释?
Am I supposed subclass GuiceContainer here? Or is there some other magic annotation that I'm missing?
这似乎是一件很普通的事情-我对使用这种guice组合被证明多么困难感到惊讶.
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文档. Guice非常易于使用,它具有很少的基本概念.您的问题在于您混合了Jersey JAX-RS依赖项注入和Guice依赖项注入.如果使用的是GuiceContainer
,则声明将对所有DI使用Guice ,因此必须使用Guice而不是JAX-RS添加绑定.
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.
例如,您不需要ContextResolver
,而应使用普通Guice Provider
:
For instance, you do not need ContextResolver
, you should use plain Guice Provider
instead:
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);
这将绑定ObjectMapper
,但是不足以将Jersey与杰克逊一起使用.您将需要某种MessageBodyReader
/MessageBodyWriter
,例如JacksonJsonProvider
.您将需要其他提供商:
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:
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);
这就是您需要做的-不需要子类化.
This is all you need to do - no subclassing is needed.
尽管有一些代码大小优化的空间.如果我是你,我会使用@Provides
-methods:
There is a room for code size optimization though. If I were you I would use @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
而不是普通的ServletModule
,它为您提供了很多有用的绑定.
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.
这篇关于如何用Guice/Jersey钩住Jackson ObjectMapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!