2013-05-26 14 views
11

Nie mogę poprawnie zarejestrować mojego modułu Jackson ObjectMapper.Jak podłączyć Jacksona ObjectMappera z Guice/Jersey

Używam stosu Guice + Jersey + Jackson (FasterXML).

Śledziłem, jak dostosować ObjectMapper na podstawie różnych pytań tutaj. W szczególności mam zadeklarowany ContextResolver, oznaczony jako @ javax.ws.rs.ext.Provider i @ javax.inject.Singleton.

Mam GuiceServletContextListener wzdłuż linii:

@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 zdefiniowany

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

Jednak - z samej tej konfiguracji getContext() nigdy nie jest wywoływana, więc odwzorowujący nie jest zarejestrowany. Utknąłem w typowej guice - adnotacjach - tajemnicach, gdzie praktycznie nie da się ich wyśledzić. Użytkownicy wiosny po prostu zgłaszają rejestrację komponentu, a kontener po prostu go podnosi.

This Odpowiedź mówi o przesłonięciu mojej własnej implementacji javax.ws.rs.core.Application. Jednak wygląda to hard-wired w impementation Jersey Guice z GuiceContainer być DefaultResourceConfig():

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

Czy mam podklasy GuiceContainer tutaj? Czy jest jeszcze jakaś magiczna adnotacja, której mi brakuje?

Wydaje się, że jest to dość powszechna rzecz - jestem zaskoczony, jak ciężko to udowodniło dzięki tej kombinacji gitar.

Odpowiedz

22

Utknąłem w typowym Guice - adnotacje-misterium, gdzie jest to praktycznie niewykrywalne do czego jestem naprawdę powinniśmy robić. Użytkownicy wiosny po prostu zgłaszają rejestrację komponentu, a kontener po prostu go podnosi.

Naprawdę powinieneś przeczytać excellent Guice documentation. Guice jest bardzo łatwy w użyciu, ma bardzo małą liczbę podstawowych pojęć. Twój problem polega na tym, że zmieszałeś wstrzyknięcie zależne od JAX-RS i zastrzyk zależności Guice. Jeśli używasz GuiceContainer, to deklarujesz, że będziesz używał Guice dla wszystkich twojego DI, więc musisz dodać powiązania z Guice, a nie z JAX-RS.

Na przykład, nie trzeba ContextResolver, należy używać zwykłego Guice Provider Zamiast:

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

Następnie należy dodać odpowiada wiązanie z modułem:

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

To będzie wiązać ObjectMapper, ale nie wystarczy użyć Jersey z Jacksonem. Będziesz potrzebował pewnego rodzaju MessageBodyReader/MessageBodyWriter, np. JacksonJsonProvider.Będziesz potrzebował innego dostawcy do niego:

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

następnie powiązać go:

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

To wszystko, co musisz zrobić - nie jest potrzebna podklasy.

Istnieje jednak możliwość optymalizacji rozmiaru kodu. Gdybym był tobą chciałbym użyć @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); 
} 

Te metody powinny być dodawane do jednego z modułów, na przykład do anonimowego ServletModule. Wtedy nie będziesz potrzebować oddzielnych klas dostawców.
BTW, powinieneś używać JerseyServletModule zamiast zwykłego ServletModule, zapewnia on wiele przydatnych powiązań.

+0

Jest to bardzo pomocne. Mój szczególny problem polegał na tym, że miałem * 2 * jacksony (zarówno wersję codehaus, jak i wersję fastxml), więc obiekty objectmappers zostały całkowicie zignorowane, ponieważ były niewłaściwego typu. – user340535

+0

Miałem ten sam problem z konfliktami zależności (codehaus + fasterxml): 'JsonIgnore' nie działało w ogóle. W moim przypadku, jackson codehaus (jackson-mapper-asl lub core-asl) był powiązany z pakietem zależnym od pakietu documentdb i został całkowicie usunięty (przez pom), aby zachować tylko najnowszą wersję (szybciejxml). Dzięki @ user340535 za komentarz, który pomoże mi naprawić hak JacksonJsonProvider! – boly38

+0

Z jakiegoś powodu wersja z adnotacjami nie działa ze mną w Guice 4 przy użyciu JerseyGuiceServletContextListener. Co może spowodować? Z normalnym wiązaniem i dodatkowymi klasami działa bez problemu. – Atais