18 de marzo de 2020

Inyección de Dependencias - @Autowired

Ya tenemos nuestro contenedor preparado con los beans que hemos cargado de distintas formas y personalizados con configuración externa. Por ahora lo máximo que hemos inyectado es una cadena de texto recuperada del fichero de propiedades tipo clave-valor. Es algo muy bueno pero no explicaría la fama de Spring ni se está invirtiendo el control.

En esta sesión vamos a inyectar beans desde nuestro contenedor usando la anotación @Autowired.

En Spring podemos realizar Inyección de Dependencias (DI) de tres formas:



En el ejemplo se están usando las anotaciones de Spring para describir cómo se inyectará nuestro bean de tipo CustomerService en nuestra clase BankingService (por constructor para campos obligatorios, por propiedad para los no obligatorios y directamente en el campo que debe evitarse).

Empezamos haciendo DI por constructor en una clase nueva es.lanyu.datosdeportivosapi.TestAutowired sobre su campo testInyectado de tipo es.lanyu.Test:
import es.lanyu.Test;

@Component("autowired")
public class TestAutowired extends Test {

    Test testInyectado;

    public TestAutowired(es.lanyu.datosdeportivosapi.Test testPorConstructor) {
        testInyectado = testPorConstructor;
    }

}
Vamos a modificar nuestro método init() para mostrar el valor de testString para nuestro bean inyectado por consola (usando el log):
import es.lanyu.Test;

@Component("autowired")
public class TestAutowired extends Test {

    private final Logger log = LoggerFactory.getLogger(TestAutowired.class);

    ...

    @Override
    public void init() {
        log.warn("Texto de bean inyectada: {}", testInyectado.getTestString());
    }
}
Veremos que nos devuelve el texto "String por defecto" ¿Por qué?: si seguimos el flujo de la aplicación cuando construya el bean "autowired" inyectará el único bean que encaja.

¿Qué pasa si tenemos más de un constructor?

Se puede marcar el que se quiere usar o marcar los que se pueden usar. Spring buscará de entre los que puede resolver el que mayor coincidencia de parámetros encuentre:
@Autowired(required=false)
public TestAutowired() {}

@Autowired(required=false)
public TestAutowired(es.lanyu.datosdeportivosapi.Test testPorConstructor) {
    testInyectado = testPorConstructor;
}

¿Qué pasa cuando varios beans encajan?

Para desambiguar en este caso debemos usar la anotación @Qualifier("nombreBean"). Si queremos inyectar el tipo es.lanyu.Test tenemos 3 candidatos posibles. Vamos a usar el bean con nombre "test" para inyectarlo en un campo:
@Autowired
@Qualifier("test")
Test testInyectado;
Ahora tenemos en testInyectado el bean "test". Spring sigue un orden al hacer DI:
  1. Usar el constructor
  2. Asignar los campos
  3. Usar los métodos de configuración (setter en nuestro caso)
No se aconseja usar la inyección de campo para hacer DI y es preferible usar el constructor para valores obligatorios y métodos para los opcionales. Éstos últimos permiten trabajar con el valor fuera de Spring al contrario que el otro: ninguno de los miembros utilizados tienen por qué ser públicos.

¿Cómo se inyecta en un método que tiene varios parámetros?

Spring va a emparejar los beans con los tipos pedidos en los parámetros. En caso de tener que desambiguar también puede usarse @Qualifier en los parámetros:
@Autowired
private void setTestInyectado(@Qualifier("config") Test testPorSetter,
                              @Qualifier("test") Test testPorSetter2) {
    testInyectado = new Test();
    testInyectado.setTestString(testPorSetter.getTestString()
                              + " y " + testPorSetter2.getTestString());
    }
}

¿No puedo elegir un bean de un tipo por defecto?

Spring permite dar preferencia a un bean con la anotación @Primary:
@Bean
@Primary
public Test miTest() {...}
No obstante, no hay ningún control global sobre las anotaciones y pueden marcarse varias que sigan produciendo conflictos.

Puedes encontrar el código hasta aquí en su repositorio y ver el vídeo del webinar:

Con esto termina la parte básica de Spring. A partir de ahora nos centraremos en desarrollar un supuesto sobre una aplicación que gestiona datos deportivos.

No hay comentarios:

Publicar un comentario

Compárteme

Entradas populares