Iniezione delle dipendenze con Jersey 2.0

Partendo da zero senza precedenti conoscenze di Jersey 1.x, ho difficoltà a capire come impostare l’iniezione di dipendenza nel mio progetto Jersey 2.0.

Capisco anche che HK2 è disponibile in Jersey 2.0, ma non riesco a trovare documenti che aiutano con l’integrazione di Jersey 2.0.

@ManagedBean @Path("myresource") public class MyResource { @Inject MyService myService; /** * Method handling HTTP GET requests. The returned object will be sent * to the client as "text/plain" media type. * * @return String that will be returned as a text/plain response. */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("/getit") public String getIt() { return "Got it {" + myService + "}"; } } @Resource @ManagedBean public class MyService { void serviceCall() { System.out.print("Service calls"); } } 

pom.xml

  2.0-rc1 UTF-8     org.glassfish.jersey jersey-bom ${jersey.version} pom import      org.glassfish.jersey.core jersey-common   org.glassfish.jersey.core jersey-server   org.glassfish.jersey jax-rs-ri   

Posso ottenere il contenitore per avviare e servire la mia risorsa, ma non appena aggiungo @Inject a MyService, il framework genera un’eccezione:

 SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions. They are: 1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128) 2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found 3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource ] with root cause org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128) at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74) 

Il mio progetto iniziale è disponibile su GitHub: https://github.com/donaldjarmstrong/jaxrs

È necessario definire un AbstractBinder e registrarlo nell’applicazione JAX-RS. Il binder specifica come l’iniezione della dipendenza dovrebbe creare le tue classi.

 public class MyApplicationBinder extends AbstractBinder { @Override protected void configure() { bind(MyService.class).to(MyService.class); } } 

Quando viene rilevato @Inject su un parametro o campo di tipo MyService.class , viene istanziato utilizzando la class MyService . Per utilizzare questo raccoglitore, è necessario registrarsi con l’applicazione JAX-RS. Nel tuo web.xml , definisci un’applicazione JAX-RS come questa:

  MyApplication org.glassfish.jersey.servlet.ServletContainer  javax.ws.rs.Application com.mypackage.MyApplication  1   MyApplication /*  

Implementa la class MyApplication (specificata sopra in init-param ).

 public class MyApplication extends ResourceConfig { public MyApplication() { register(new MyApplicationBinder()); packages(true, "com.mypackage.rest"); } } 

Il raccoglitore che specifica l’iniezione di dipendenza è registrato nel costruttore della class e diciamo anche all’applicazione dove trovare le risorse REST (nel tuo caso, MyResource ) usando la chiamata al metodo packages() .

Innanzitutto basta rispondere a un commento nella risposta accetta.

“Cosa fa il binding? Cosa succede se ho un’interfaccia e un’implementazione?”

Legge semplicemente bind( implementation ).to( contract ) . È ansible la catena alternativa .in( scope ) . Ambito predefinito di PerLookup . Quindi se vuoi un singleton, puoi

 bind( implementation ).to( contract ).in( Singleton.class ); 

È disponibile anche un RequestScoped

Inoltre, invece di bind(Class).to(Class) , puoi anche bind(Instance).to(Class) , che sarà automaticamente un singleton.


Aggiungendo alla risposta accettata

Per coloro che cercano di capire come registrare l’implementazione di AbstractBinder nel tuo web.xml (cioè non stai utilizzando un ResourceConfig ), sembra che il raccoglitore non venga scoperto tramite la scansione dei pacchetti, cioè

 org.glassfish.jersey.servlet.ServletContainer  jersey.config.server.provider.packages  your.packages.to.scan   

O anche questo

  jersey.config.server.provider.classnames  com.foo.YourBinderImpl   

Per farlo funzionare, ho dovuto implementare una Feature :

 import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; import javax.ws.rs.ext.Provider; @Provider public class Hk2Feature implements Feature { @Override public boolean configure(FeatureContext context) { context.register(new AppBinder()); return true; } } 

L’annotazione @Provider dovrebbe consentire alla funzione di essere rilevata dalla scansione del pacchetto. O senza la scansione dei pacchetti, è ansible registrare esplicitamente la Feature nel web.xml

  Jersey Web Application org.glassfish.jersey.servlet.ServletContainer  jersey.config.server.provider.classnames  com.foo.Hk2Feature   ... 1  

Guarda anche:

  • Iniezione del parametro del metodo personalizzato con Jersey
  • Come iniettare un object nel contesto della richiesta di jersey?
  • Come configurare correttamente un EntityManager in un’applicazione jersey / hk2?
  • Richiedi Iniezione scopata in Singletons

e per informazioni generali dalla documentazione di Jersey

  • Iniezione personalizzata e gestione del ciclo di vita

AGGIORNARE

fabbriche

Oltre al binding di base nella risposta accettata, hai anche delle fabbriche, dove puoi avere una logica di creazione più complessa e avere anche accesso alle informazioni di contesto della richiesta. Per esempio

 public class MyServiceFactory implements Factory { @Context private HttpHeaders headers; @Override public MyService provide() { return new MyService(headers.getHeaderString("X-Header")); } @Override public void dispose(MyService service) { /* noop */ } } register(new AbstractBinder() { @Override public void configure() { bindFactory(MyServiceFactory.class).to(MyService.class) .in(RequestScoped.class); } }); 

Quindi puoi inserire MyService nella tua class di risorse.

La risposta selezionata risale a qualche tempo fa. Non è pratico dichiarare ogni associazione in un raccoglitore HK2 personalizzato. Sto usando Tomcat e ho dovuto aggiungere solo una dipendenza. Anche se è stato progettato per Glassfish, si adatta perfettamente ad altri contenitori.

   org.glassfish.jersey.containers.glassfish jersey-gf-cdi ${jersey.version}  

Assicurati che anche il tuo contenitore sia configurato correttamente ( consulta la documentazione ).

In ritardo ma spero che questo aiuti qualcuno.

Ho il mio JAX RS così definito:

 @Path("/examplepath") @RequestScoped //this make the diference public class ExampleResource { 

Quindi, nel mio codice finalmente posso iniettare:

 @Inject SomeManagedBean bean; 

Nel mio caso, il SomeManagedBean è un bean ApplicationScoped.

Spero che questo aiuti a nessuno.

Oracle consiglia di aggiungere l’annotazione @Path a tutti i tipi da iniettare quando si combina JAX-RS con CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Anche se questo è tutt’altro che perfetto ( ad esempio riceverai avvertimenti da Jersey all’avvio), ho deciso di seguire questa strada, il che mi risparmia dal mantenere tutti i tipi supportati all’interno di un raccoglitore.

Esempio:

 @Singleton @Path("singleton-configuration-service") public class ConfigurationService { .. } @Path("my-path") class MyProvider { @Inject ConfigurationService _configuration; @GET public Object get() {..} } 

Se preferisci usare Guice e non vuoi dichiarare tutti i binding, puoi provare anche questo adattatore:

guice-ponte-jit-iniettore