jersey
Inyección de dependencia con jersey
Buscar..
Inyección de dependencia básica utilizando HK2 de Jersey
Jersey (2) utiliza HK2 como su sistema de inyección de dependencia (DI). Podemos usar otros sistemas de inyección, pero su infraestructura está construida con HK2 y nos permite usarla también dentro de nuestras aplicaciones.
Configurar una inyección de dependencia simple con Jersey requiere solo unas pocas líneas de código. Digamos, por ejemplo, que tenemos un servicio que nos gustaría inyectar en nuestros recursos.
public class GreetingService {
public String getGreeting(String name) {
return "Hello " + name + "!";
}
}
Y queremos inyectar este servicio en un recurso de Jersey.
@Path("greeting")
public class GreetingResource {
@Inject
public GreetingService greetingService;
@GET
public String get(@QueryParam("name") String name) {
return this.greetingService.getGreeting(name);
}
}
Para que la inyección funcione, todo lo que necesitamos es una configuración simple.
@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(GreetingResource.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(GreetingService.class);
}
});
}
}
Aquí estamos diciendo que queremos vincular el GreetingService
al sistema de inyección y anunciarlo como inyectable por la misma clase. Lo que significa la última declaración es que solo podemos inyectarlo como GreetingService
y (probablemente obviamente) no por ninguna otra clase. Como verás más adelante, es posible cambiar esto.
Eso es. Que todo lo que necesitas. Si no está familiarizado con esta configuración de ResourceConfig
(quizás esté usando web.xml), consulte el tema de configuración de JAX-RS en Jersey en SO Docs.
Nota: la inyección anterior es la inyección de campo, donde el servicio se inyecta en el campo del recurso. Otro tipo de inyección es la inyección de constructor, donde el servicio se inyecta en el constructor
private final GreetingService greetingService;
@Inject
public GreetingResource(GreetingService greetingService) {
this.greetingService = greetingService;
}
Esta es probablemente la forma preferida de ir en lugar de la inyección de campo, ya que facilita la prueba unitaria del recurso. La inyección de constructor no requiere ninguna configuración diferente.
Ok, ahora digamos que en lugar de una clase, GreetingService
es una interfaz, y tenemos una implementación de la misma (que es muy común). Para configurar eso, usaríamos la siguiente sintaxis en el método de configure
anterior
@Override
protected void configure() {
bind(NiceGreetingService.class).to(GreetingService.class);
}
Esto se lee como "vincular NiceGreetingService
, y publicitarlo como GreetingService
". Esto significa que podemos usar el mismo código exacto en el GreetingResource
anterior, porque anunciamos el contrato como GreetingService
y no como NiceGreetingService
. Pero la implementación real, cuando se inyecte, será el NiceGreetingService
.
Ahora qué pasa con el alcance. Si alguna vez ha trabajado con un marco de inyección, se habrá topado con el concepto de alcance, que determina la vida útil del servicio. Es posible que haya oído hablar de un "Ámbito de solicitud", donde el servicio está activo solo durante la vida útil de la solicitud. O un "Alcance de Singleton", donde solo hay una instancia del servicio. Podemos configurar estos ámbitos también utilizando la siguiente sintaxis.
@Override
protected void configure() {
bind(NiceGreetingService.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
El alcance predeterminado es PerLookup
, lo que significa que cada vez que se solicite este servicio, se creará uno nuevo. En el ejemplo anterior, utilizando RequestScoped
, se RequestScoped
un nuevo servicio para una sola solicitud. Esto puede o no puede ser lo mismo que el PerLookup
, dependiendo de cuántos lugares estamos tratando de inyectarlo. Podemos estar intentando inyectarlo en un filtro y en un recurso. Si esto fuera PerLookup
, entonces se PerLookup
dos instancias para cada solicitud. En este caso, solo queremos uno.
Los otros dos ámbitos disponibles son Singleton
(solo se creó una instancia) e Immediate
(como Singleton
) pero se crea en el inicio (mientras que con Singleton
, no se crea hasta la primera solicitud).
Aparte de las clases vinculantes, también podríamos usar una instancia. Esto nos da un producto único defecto, así que nosotros no necesita usar la in
sintaxis.
@Override
protected void configure() {
bind(new NiceGreetingService())
.to(GreetingService.class);
}
¿Qué sucede si tenemos alguna lógica de creación compleja o necesitamos información de contexto de solicitud para el servicio? En este caso hay Factory
s. La mayoría de las cosas que podemos inyectar en nuestros recursos de Jersey, también podemos inyectarlas en una Factory
. Tomar como ejemplo
public class GreetingServiceFactory implements Factory<GreetingService> {
@Context
UriInfo uriInfo;
@Override
public GreetingService provide() {
return new GreetingService(
uriInfo.getQueryParameters().getFirst("name"));
}
@Override
public void dispose(GreetingService service) {
/* noop */
}
}
Aquí tenemos una fábrica, que obtiene información de solicitud de UriInfo
, en este caso, un parámetro de consulta, y creamos el Servicio de GreetingService
partir de él. Para configurarlo, utilizamos la siguiente sintaxis.
@Override
protected void configure() {
bindFactory(GreetingServiceFactory.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
Eso es. Estos son sólo los conceptos básicos. Hay muchas más cosas que HK y Jersey tienen que hacer.