Inyección de dependencia en Jakarta EE

La inyección de dependencia es un patrón que permite que las clases reciban los objetos colaboradores de los que depende como parámetro, en vez de gestionarse de forma interna y local. En la especificación de Jakarta para inyección de dependencia se definen algunas anotaciones que se pueden usar para usar este patrón en aplicaciones de este tipo.

Cuando trabajamos en proyectos con Jakarta EE o Java Enterprise, integrar la inyección de dependencia es una práctica que nos ayuda a crear servicios reutilizables, mantenibles y fáciles de testear. La inyección de dependencia nos permite separar la lógica de negocio en servicios independientes, lo que facilita su reutilización y mejora la arquitectura general de la aplicación.

Para ilustrar esto, podemos empezar creando un servicio sencillo que se encargue de saludar. Por ejemplo, definimos una clase llamada GritterService que contiene un método grit que recibe un nombre y devuelve un saludo personalizado:

public class GritterService {
    public String grit(String name) {
        return "hello " + name;
    }
}

Este servicio es un POJO (Plain Old Java Object), lo que significa que es una clase Java simple sin dependencias externas. Esto lo hace muy fácil de testear con frameworks como JUnit, ya que podemos verificar que el método grit devuelve el saludo esperado para diferentes entradas.

Ahora, para integrar este servicio en un recurso REST, imaginemos que tenemos un endpoint que responde a peticiones en /hello. En lugar de instanciar directamente el servicio dentro del método que maneja la petición, lo que haríamos es definir un atributo de clase para el servicio y utilizar la inyección de dependencia para que el framework nos proporcione la instancia adecuada. Por ejemplo:

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("/hello")
public class GrittingResource {

    private final GritterService gritter;

    @Inject
    public GrittingResource(GritterService gritter) {
        this.gritter = gritter;
    }

    @GET
    public String hello(@QueryParam("name") String name) {
        return gritter.grit(name);
    }
}

Aquí, el constructor recibe una instancia de GritterService que es inyectada automáticamente por el contenedor de Jakarta EE o el framework que estemos usando, como Quarkus. Esto sigue el principio SOLID de inversión de dependencias, donde las dependencias de una clase se proporcionan desde fuera, en lugar de ser creadas internamente.

Para que el contenedor sepa cómo crear y gestionar la instancia de GritterService, debemos anotarla con una de las anotaciones que definen su alcance y ciclo de vida. Por ejemplo, podemos usar @Singleton para indicar que queremos una única instancia compartida durante toda la aplicación:

import javax.inject.Singleton;

@Singleton
public class GritterService {
    public String grit(String name) {
        return "hello " + name;
    }
}

Con esta configuración, cuando el recurso GrittingResource sea instanciado, el contenedor inyectará automáticamente el GritterService singleton. Esto evita que tengamos que crear manualmente el servicio dentro del recurso y nos permite cambiar fácilmente la implementación del servicio sin modificar el recurso.

Además, esta forma de trabajar facilita el testing. Por ejemplo, podemos crear una clase que extienda GritterService y sobrescriba el método grit para devolver un valor fijo o simulado, lo que nos permite hacer pruebas unitarias sin depender de servicios externos o lógica compleja:

public class FakeGritterService extends GritterService {
    @Override
    public String grit(String name) {
        return "hmmm";
    }
}

Luego, en los tests, podemos inyectar esta versión falsa para controlar el comportamiento del servicio y verificar que el recurso responde correctamente.

En resumen, la inyección de dependencia en Jakarta EE se basa en anotar las clases proveedoras con anotaciones como @Singleton o @ApplicationScoped para que el contenedor las gestione, y en usar @Inject para que las dependencias se inyecten automáticamente en los constructores o atributos de las clases consumidoras. Esto nos permite construir aplicaciones más modulares, fáciles de mantener y con un mejor soporte para pruebas automatizadas.

Lista de reproducción
  1. 1
    ¿Qué es Jakarta EE?
    7 minutos
  2. 2
    ¿Qué diferencia hay entre JakartaEE y JavaEE?
    5 minutos
  3. 3
    ¿Qué diferencia hay entre Spring® y JakartaEE?
    6 minutos
  4. 4
    Inyección de dependencia en Jakarta EE
    11 minutos
  5. 5
    Anotaciones para aplicar inyección de dependencia en Jakarta
    13 minutos