Spring Inyección automática con autowiring

El contenedor Spring posee un mecanismo de inyección automática llamado autowiring, este mecanismo inspecciona el ApplicationContext en busca de las dependencias requeridas por los beans y las inyecta automáticamente, contamos con tres modos disponibles: byType, byName, y constructor.

En el tutorial configuración XML aprendimos como utilizar <constructor-arg /> y <property /> para inyectar las dependencias, si tenemos un bean A que necesita una referencia a un bean B podemos establecer esta relación de manera automática con el atributo autowire del elemento <bean />.

public class EmpleadoDaoImpl implements EmpleadoDao {
   //...//
}

public class EmpleadoServiceImpl implements EmpleadoService {

    private EmpleadoDao empleadoDao;

    public EmpleadoServiceImpl() {
    }

    public EmpleadoServiceImpl(EmpleadoDao empleadoDao) {
        this.empleadoDao = empleadoDao;
    }

    public void setEmpleadoDao(EmpleadoDao empleadoDao) {
        this.empleadoDao = empleadoDao;
    }

}

Primero veamos como seria la configuración de estos bean sin utilizar autowiring, por defecto este mecanismo esta desactivado, con XML la configuración quedaría de la siguiente manera:

<bean id="empleadoDao"
      class="carmelo.spring.autowiring.EmpleadoDaoImpl" />
    
<bean id="empleadoService" 
      class="carmelo.spring.autowiring.EmpleadoServiceImpl">
    
    <property name="empleadoDao" ref="empleadoDao" />
    
</bean>

El bean empleadoService necesita una referencia a empleadoDao, en este ejemplo la inyectamos usando el método setEmpleadoDao, pudimos haberlo hecho mediante el constructor, lo importante es que la inyección la indicamos nosotros, utilizando autowiring esto será automático.  

Autowiring byName

Utilizando autowiring en el modo byName Spring buscará un bean que encaje con el nombre de la propiedad, si lo encuentra lo inyectará de lo contrario la propiedad tendrá un valor nulo, para activar este mecanismo utilizamos autowire="byName".

<bean id="empleadoService" 
      class="carmelo.spring.autowiring.EmpleadoServiceImpl"
      autowire="byName" />   

<bean id="empleadoDao"
      class="carmelo.spring.autowiring.EmpleadoDaoImpl" />

Como empleadoService tiene un método setEmpleadoDao Spring intentará buscar un bean que se llame empleadoDao, el mismo nombre del método sin el set y la primera letra en minúscula, si lo encuentra, lo inyecta.

Autowiring byType

Parecido al anterior, el contenedor Spring detecta el método setEmpleadoDao(EmpleadoDao eDao) e intentará inyectar un bean de tipo EmpleadoDao, esta vez el bean será buscado por su tipo, en este ejemplo, bebe ser un bean que implemente la interface EmpleadoDao, para usarlo establecemos el atributo autowire="byType".

<bean id="empDaoBean"
      class="carmelo.spring.autowiring.EmpleadoDaoImpl" />
    
<bean id="empleadoService" 
      class="carmelo.spring.autowiring.EmpleadoServiceImpl"
      autowire="byType" />

En este modo el nombre del bean no es significante para la inyección automática, lo importante es que el tipo encaje en el método setter correspondiente.

Autowiring constructor

Se aplica al constructor de la clase, el contenedor Spring identificará cada uno de los argumentos del constructor por tipo e intentará inyectar los bean que correspondan a cada uno de estos argumentos, si no se puede satisfacer los argumentos de alguno de los constructores presentes en la clase se producirá una excepción, para usarlo establecemos el atributo autowire="constructor"

<bean id="empDaoBean"
      class="carmelo.spring.autowiring.EmpleadoDaoImpl" />
    
<bean id="empleadoService" 
      class="carmelo.spring.autowiring.EmpleadoServiceImpl"
      autowire="constructor" />

Nuestra clase EmpleadoServiceImpl tiene dos constructores, uno sin argumentos o otro que requiere un argumento de tipo EmpleadoDao, este constructor se puede inyectar ya que tenemos un bean de este tipo, este será el constructor elegido por Spring.

Autowiring con código Java

Si deseamos utilizar el autowiring con la configuración por código Java, solo debemos modificar el campo autowire de la anotación @Bean e indicar cual modo deseamos utilizar, tenemos disponibles: Autowire.BY_TYPE o Autowire.BY_NAME.

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringJavaConfig {

    @Bean
    public EmpleadoDao empleadoDao() {
        return new EmpleadoDaoImpl();
    }

    @Bean(autowire = Autowire.BY_TYPE)
    public EmpleadoService empleadoService() {
        return new EmpleadoServiceImpl();
    }
}

En este ejemplo utilizamos en autowiring por tipo, si deseamos utilizarlo por nombre solo cambiamos el modo, recordemos que el nombre del bean es igual al nombre del método, salvo que se indique un nombre en la anotación, ejemplo: @Bean(name = "empleadoDaoJpa").

Nótese que en ningún momento tuvimos que pasar una referencia del objeto EmpleadoDao a EmpleadoService ni usando el constructor o el correspondiente método setter, de esta tarea se encarga el contenedor Spring al activar el autowiring.

Usando la anotación @Autowired

Si usamos la configuración por anotaciones disponemos de @Autowired para habilitar la inyección automática, esta anotación puede ser aplicada a nivel de constructor o método setter, vamos a modificar nuestro ejemplo para utilizar la configuración mediante anotaciones.

Primero habilitamos el escaneo de componentes en la configuración XML con la etiqueta <context:component-scan base-package="..."/> o usando la configuración mediante código Java con la anotación @ComponentScan(basePackages = "...").

package carmelo.spring.autowiring;

import org.springframework.stereotype.Repository;

@Repository("empleadoDao")
public class EmpleadoDaoImpl implements EmpleadoDao {

    @Override
    public String toString() {
        return "Logica de acceso a datos...";
    }

}

Utilizamos la anotación @Repository para indicar que este es un bean de la capa de acceso a datos, @Service para un bean de la capa de servicios, la segunda clase se ve del siguiente modo:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("empleadoService")
public class EmpleadoServiceImpl implements EmpleadoService {

    private EmpleadoDao empleadoDao;

    public EmpleadoServiceImpl() {
        
    }
    
    @Autowired
    public EmpleadoServiceImpl(EmpleadoDao empleadoDao) {
        this.empleadoDao = empleadoDao;
    }

    public void setEmpleadoDao(EmpleadoDao empleadoDao) {
        this.empleadoDao = empleadoDao;
    }
    
    public EmpleadoDao getEmpleadoDao() {
        return empleadoDao;
    }

}

Podemos ver que la anotación @Autowired esta sobre el constructor lo que habilita el autowiring precisamente utilizando el constructor, las dependencias correspondientes se buscan por tipo,  si colocamos la anotación sobre el método setEmpleadoDao la inyección se hará usando este método.

Si la dependencia no se puede resolver, es decir no existe un bean que pueda satisfacer la propiedad o constructor, se producirá una excepción, podemos usar @Autowired(required=false) para indicar que esta es una dependencia opcional, si no hay un bean disponible que inyectar no se producirá la excepción.

En caso de que no tengamos el método o el constructor podemos colocar la anotación @Autowired sobre el campo empleadoDao.

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

Controles y Contenedores JavaFX 8 - I

tkinter Canvas

Histogramas OpenCV Python