2013-05-07 20 views
5

Trudno mi zrozumieć mechanizm wtryskiwania Jersey. Specyfikacja JAX-RS (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) stwierdza, że ​​wstrzykiwanie przez @Context jest możliwe w podklasach aplikacji, klasach zasobów źródłowych i dostawcach.Jersey @Context zakres

Mam teraz klasę, która jest tworzona przy uruchomieniu i ma metodę, która jest wywoływana na każde żądanie. Wewnątrz metody potrzebuję dostępu do bieżącego obiektu UriInfo. Problem polega na tym, że ta metoda nie jest wywoływana z mojego kodu. Więc nie mogę przekazać UriInfo bezpośrednio do metody.

I rzeczywiście chcą zrobić coś takiego:

public class MyClass implements ThirdPartyInterface { 

    // not possible because class is no Application subclass, root resource class or provider 
    @Context 
    private UriInfo uriInfo; 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

próbowałem tego. Oczywiście bez powodzenia:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public MyClass(UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     // uriInfo is null at this time :(
     myClass = new MyClass(uriInfo); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 
    } 
} 

Jedyne obejście, jakie mogę wymyślić, to jest to. Nie sądzę, że to jest bardzo czyste:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public void setUriInfo(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     myClass = new MyClass(); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     myClass.setUriInfo(uriInfo); 

     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 

     myClass.setUriInfo(null); 
    } 
} 

Mam nadzieję, że istnieje lepsze rozwiązanie, ale być może jestem na niewłaściwym torze.

Dzięki!

+0

Może po prostu potrzebujesz 'ContainerRequestFilter'? – Willy

+0

Nie wiem, czy to działa w mojej sytuacji. Specyfikacja mówi, że "klasy dostawcy są tworzone przez środowisko wykonawcze JAX-RS". Ale potrzebuję odniesienia do obiektu w czasie budowy, aby przekazać go do usługi strony trzeciej. –

Odpowiedz

3

Późne odpowiedź, ale dobre pytanie ... więc puszcza:

Można użyć org.glassfish.hk2.api.Factory i javax.inject.Provider do wstrzykiwań. Nie wiem, od kiedy to wersja jest dostępna, więc może musisz uaktualnić swoją wersję jersery. Dla następujących próbek użyłem jersey 2.12.

Najpierw trzeba wdrożyć i zarejestrować/powiązać Fabrykę dla Twojego MyClass:

MyClassFactory:

import javax.inject.Inject; 
import javax.ws.rs.core.UriInfo; 
import org.glassfish.hk2.api.Factory; 
// ... 

public class MyClassFactory implements Factory<MyClass> { 

    private final UriInfo uriInfo; 

    // we will bind MyClassFactory per lookup later, so 
    // the constructor will be called everytime we need the factory 
    // meaning, uriInfo is also per lookup 

    @Inject 
    public MyClassFactory(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public MyClass provide() { 
     return new MyClass(uriInfo) 
    } 

    @Override 
    public void dispose(UriInfo uriInfo) { 
     // ignore 
    } 

} 

Rejestracja poprzez ResourceConfig:

import org.glassfish.hk2.api.PerLookup; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.server.ResourceConfig; 
// ... 

public class MyResourceConfig extends ResourceConfig { 

    public MyResourceConfig() { 
     register(new AbstractBinder() { 
      @Override 
      protected void configure() { 
       bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class); 
       // ... bind additional factories here 
      } 
     }); 
     // ... 
    } 

} 

Teraz jesteś w stanie wstrzyknąć MojaKlasa na wyszukiwanie do dostawców, zasobów itp.
Ale Uwaga: Afaig istnieją dwa podejścia es i tylko jeden będzie działać, jak ostatecznie aspected dla dostawców ...

import javax.inject.Inject; 
import javax.ws.rs.Produces; 
import javax.ws.rs.ext.MessageBodyWriter; 
import javax.ws.rs.ext.Provider; 
// ... 

@Provider 
@Produces("application/foo-bar") 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    // first approache - don't do it! 
    // will only injected once, cause MyBodyWriter is only instantiated once 
    @Inject 
    private MyClass myClass; 

    // second approache - works fine! 
    private final javax.inject.Provider<MyClass> provider; 

    // MyBodyWriter instantiate once 
    // get an inject provider here 
    @Inject 
    public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) { 
     this.provider = myClassProvider; 
    } 

    @Override 
    public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) { 
     return t == MyView.class; 
    } 

    @Override 
    public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) { 
     // deprecated by JAX-RS 2.0 and ignored by Jersey runtime 
     return 0; 
    } 

    @Override 
    public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException { 

     // attention: its not per lookup !!! 
     MyClass myClassDirectInjected = myClass; 
     System.out.println(myClassDirectInjected); // same instance everytime 

     // but this is ;) 
     MyClass myClassFromProvider = provider.get(); 
     System.out.println(myClassFromProvider); // it's a new instance everytime 

     // ... 
    } 

} 

Mam nadzieję, że to było jakoś pomocne.