java - Inyección de dependencia con Jersey 2.0
dependency-injection jersey-2.0 (6)
Debe definir AbstractBinder
y registrarlo en su aplicación JAX-RS. El cuaderno especifica cómo la inyección de dependencia debería crear sus clases.
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(MyService.class).to(MyService.class);
}
}
Cuando se detecta @Inject
en un parámetro o campo de tipo MyService.class
se MyService.class
una instancia utilizando la clase MyService
. Para usar esta carpeta, debe registrarse con la aplicación JAX-RS. En su web.xml
, defina una aplicación JAX-RS como esta:
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.mypackage.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Implemente la clase MyApplication
(especificada arriba en init-param
).
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages(true, "com.mypackage.rest");
}
}
El enlazador que especifica la inyección de dependencia está registrado en el constructor de la clase, y también le indicamos a la aplicación dónde encontrar los recursos REST (en su caso, MyResource
) utilizando la llamada al método packages()
.
Empezando desde cero sin ningún conocimiento previo de Jersey 1.x, me está resultando difícil entender cómo configurar la inyección de dependencia en mi proyecto Jersey 2.0.
También entiendo que HK2 está disponible en Jersey 2.0, pero parece que no puedo encontrar documentos que ayuden con la integración de 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
<properties>
<jersey.version>2.0-rc1</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jax-rs-ri</artifactId>
</dependency>
</dependencies>
Puedo hacer que el contenedor se inicie y sirva mi recurso, pero tan pronto como agregue @Inject a MyService, el marco arroja una excepción:
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)
Mi proyecto de inicio está disponible en GitHub: https://github.com/donaldjarmstrong/jaxrs
La respuesta seleccionada data de hace un tiempo. No es práctico declarar cada encuadernación en una carpeta personalizada HK2. Estoy usando Tomcat y solo tuve que agregar una dependencia. Aunque fue diseñado para Glassfish, se adapta perfectamente a otros contenedores.
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>
Asegúrese de que su contenedor esté configurado correctamente también ( consulte la documentación ).
Oracle recomienda agregar la anotación @Path a todos los tipos para combinar cuando se combina JAX-RS con CDI: http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm Aunque esto dista mucho de ser perfecto ( por ejemplo, recibirás una advertencia de Jersey al inicio), decidí tomar esta ruta, lo que me ahorra mantener todos los tipos admitidos dentro de una carpeta.
Ejemplo:
@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
..
}
@Path("my-path")
class MyProvider {
@Inject ConfigurationService _configuration;
@GET
public Object get() {..}
}
Primero solo para responder un comentario en la respuesta de aceptación.
"¿Qué hace bind? ¿Qué pasa si tengo una interfaz y una implementación?"
Simplemente lee bind( implementation ).to( contract )
. Puede cadena alternativa .in( scope )
. Alcance predeterminado de PerLookup
. Entonces, si quieres un singleton, puedes
bind( implementation ).to( contract ).in( Singleton.class );
También hay un RequestScoped
disponible
Además, en lugar de bind(Class).to(Class)
, también puede bind(Instance).to(Class)
, que será automáticamente un singleton.
Agregando a la respuesta aceptada
Para aquellos que intentan descubrir cómo registrar su implementación de AbstractBinder
en su web.xml (es decir, no está utilizando ResourceConfig
), parece que la carpeta no se descubrirá mediante el escaneo de paquetes, es decir,
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
your.packages.to.scan
</param-value>
</init-param>
O esto tampoco
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.YourBinderImpl
</param-value>
</init-param>
Para que funcione, tuve que implementar 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;
}
}
La anotación @Provider
debe permitir que la Feature
sea detectada por el escaneo del paquete. O sin escaneo de paquetes, puede registrar explícitamente la Feature
en el web.xml
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.Hk2Feature
</param-value>
</init-param>
...
<load-on-startup>1</load-on-startup>
</servlet>
Ver también:
- Inyección de parámetros de método personalizado con Jersey
- ¿Cómo inyectar un objeto en el contexto de solicitud jersey?
- ¿Cómo configuro correctamente un EntityManager en una aplicación jersey / hk2?
- Solicitud de inyección de ámbito en Singletons
y para información general de la documentación de Jersey
ACTUALIZAR
Suerte
Además del enlace básico en la respuesta aceptada, también tiene fábricas, donde puede tener una lógica de creación más compleja y también tiene acceso para solicitar información de contexto. Por ejemplo
public class MyServiceFactory implements Factory<MyService> {
@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);
}
});
Luego puede inyectar MyService
en su clase de recursos.
Si prefiere usar Guice y no desea declarar todas las vinculaciones, también puede probar este adaptador:
Tarde pero espero que esto ayude a alguien.
Tengo mi JAX RS definido así:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {
Entonces, en mi código finalmente puedo inyectar:
@Inject
SomeManagedBean bean;
En mi caso, SomeManagedBean
es un bean ApplicationScoped.
Espero que esto ayude a cualquiera.