Inyección de dependencias II

En la entrada anterior Inyección de dependencias I  implementamos este proceso a través del constructor de la clase. Ahora revisaremos los demás métodos que tenemos para inyectar beans, mediante un método setter y mediante cableado automático (autowiring).

Vamos a realizar algunas modificaciones al proyecto anterior para implementar los temas anteriormente mencionados.

Contenido

Inyección mediante método setter

Reglas que se deben cumplir para utilizar este método de inyección.

  • La clase debe tener un constructor vacio( sin parametros), esto no implica que no pueda tener constructores con parametros.
  • El método setter debe ser public, sin retorno(void), y debe nombrarse igual al atributo precedido por palabra set, ejem: para un atributo edad, public void setEdad(...)

Clase Esfero

En la clase Esfero creada anteriormente, agregamos un constructor vacío y el método setter para el atributo tinta.

public class Esfero{
    // .. Codigo anterior
    
     // Constructor vacio 
	public  Esfero(){}
    
     // Metodo setter
	public void setTinta(Tinta tinta ){
    		this.tinta = tinta;   	
    	}
}

Ya no será necesario mantener el constructor parametrizado de la entrada anterior, si deseas lo puedes conservar o eliminar.

Contenedor Spring

 <beans>
  	<--beans anteriores --> 
    	<bean id="miEsfero" class="com.curso.spring.Esfero"> 		
            	<!--Inyección mediante constructor 
                 <constructor-arg  ref="miTintaAzul" > </constructor-arg> -->
                 
         	<!--Inyección mediante setter -->    
  		   <property name="tinta" ref="miTinta" > </property>         
        </bean>
    			  
</beans>

Reemplazamos <constructor-arg > por <property> para inyectar mediante método setter, el atributo name corresponde al nombre del metodo setter de Esfero sin la palabra set( ejem: setTinta --> tinta) y ref corresponde a un bean Tinta creado anteriormente. Puedes utilizar varias <property> para inyectar beans.

Puedes combinar <constructor-arg > y <property>, una buena practica es utilizar <constructor-arg > para beans que son obligatorio y <property> para beans opcionales.

Cableado automático(autowire)

Permite al contenedor Spring conectar automáticamente las relaciones(dependencias) entre los beans. El cableado automático reduce significativamente la necesidad de especificar argumentos mediante <constructor-arg > o <property>.

Tenemos 4 modos para el cableado automático:

  • Default: (por defecto), sin cableado automático, debe utilizar <constructor-arg > o <property> para la inyección.
  • byName: autocableado por nombre de propiedad. El Contenedor Spring buscara un bean con el mismo nombre(id) que la propiedad que debe conectarse, utiliza un metodo setter.
  • byType: Conecta una propiedad automáticamente si existe un unico bean del tipo de la propiedad en el contenedor.Si existe más de uno, se lanza un error fatal. Utiliza un método setter para inyectar, no es obligatorio que el setter sea nombrado igual a la propiedad precedida de set.
  • constructor: Similar a byType pero se aplica a los argumentos del constructor. Si no existe un bean del tipo de argumento del constructor en el contenedor, se generara un error fatal.

Para especificar el modo de conexión automática para un bean utilizamos el atributo autowire del elemento <bean>

Te recomiendo que revises la sección cableado automático de Spring para ver más detalles.

Cableado automático por constructor

 <beans>
  	<--beans anteriores --> 
    	<bean id="miEsfero" class="com.curso.spring.Esfero"  autowire="constructor" > 		          
        </bean>
    			  
</beans>

Hemos agregado autowire utilizando constructor para inyectar un bean Tinta, sustituyendo el uso <constructor-arg >.

El contenedor Spring tomará un bean que coincida con el tipo y nombre del parámetro del constructor y lo inyectará. De lo contrario no inyectara ningún bean.

Detalles a tener presente para nuestro ejemplo

Tenemos tres beans tipo Tinta (miTinta, miTintaAzul, miTintaRoja). Ninguno coincide con el nombre del parámetro del constructor Esfero: public Esfero (Tinta tinta), por ello no se inyectará ningún bean Tinta y dará un error NullPointerException al llamar al método verColor() en la clase principal. Tenemos tres soluciones:

  • Renombrar uno de los bean Tinta del contenedor: por ejemplo, miTinta renómbrarlo a tinta para que coincida con nombre del parámetro del constructor
  • Renombrar el parámetro del constructor: parámetro tinta renómbrarlo algún beans del contenedor (miTinta, miTintaAzul, miTintaRoja). Es lo inverso a la primera solución.
  • Si no desea renombrar ninguno de los beans Tinta, agregue el atributo primary= "true" en uno de los <bean> Tinta, Spring tomará ese bean y lo inyectará, solo puede haber un primary= "true" en beans del mismo tipo.

Cableado automático por tipo(byType)

 <beans>
  	<--beans anteriores --> 
    	<bean id="miEsfero" class="com.curso.spring.Esfero"  autowire="byType" > 		          
        </bean>
    			  
</beans>

El contenedor Spring creará el bean miEsfero y luego verificará si tiene alguna dependencia establecida mediante método setter, si es así, tomará un bean que coincida con el tipo del parámetro del método setter y lo inyectará. Si existe más de un bean candidato (ósea del mismo tipo), lanzara un error tipo UnsatisfiedDependencyException, ya que no sabe que bean debe inyectar.

Detalles a tener presente para nuestro ejemplo

Los beans miTintamiTintaAzul, miTintarRoja son del mismo tipo Tinta(Implementan Interface Tinta) ósea tenemos más de un bean para ser inyectado, dará un error. Tenemos dos soluciones

  • Eliminar dos de los beans Tinta, con ello solo tendremos un único candidato para ser inyectado.
  • Agregar el atributo primary = true en uno de los <bean> Tinta

Cableado automático por nombre(byName)

 <beans>
  	<--beans anteriores --> 
    	<bean id="miEsfero" class="com.curso.spring.Esfero"  autowire="byName" > 		          
        </bean>
    			  
</beans>

Similar a la inyección byType, pero el proceso para inyectar el bean cambia, Spring tomará un bean del contenedor cuyo nombre coincida con el nombre de un metodo setter (sin el prefijo set) del bean miEsfero.

Detalles a tener presente para nuestro ejemplo

En Esfero tenemos el método setter llamado setTinta(…) para inyectar la Tinta, pero el nombre del método no coincide con ninguno de los beans Tinta (miTinta, miTintaAzul, miTintaRoja) del contenedor, por ende, debemos renombrar uno de ellos. Por ejemplo, miTinta llamarlo tinta, ahora si coincidirá con  setTinta(sin tener en cuenta prefijo set).

Excluir un bean para el cableado automático

Utilice el atributo autowire-candidate = false en el elemento <bean>. Esto hará que esa definición de bean especifica no esté disponible para el cableado automático. Puede serle útil cuando utilice el modo de cableado byType.

Definir un bean principal

Con el atributo primary= true sobre un elemento <bean>, hará que esa definición de bean sea el bean principal a inyectar en caso que Spring encuentra un problema de ambigüedad  al momento de resolver una dependencia.

Tenga en cuenta que si define dependencias explicitas con <property> y <constructor-arg> siempre anulan el cableado automático.

Descargar Proyecto github

¡Hasta pronto 👋👍!

Comentarios

Entradas populares de este blog

JWT (Json Web Token)

Instalar Java Developmet Kit(JDK)

Curso de Spring