El mecanismo de autoensamblado (autowire) en Spring surge como respuesta a un problema recurrente en la configuración XML tradicional: la proliferación excesiva de etiquetas <property> dentro de cada definición de bean. Cuando un componente requiere múltiples dependencias, el archivo de configuración se vuelve extenso, difícil de leer y propenso a errores.
Ejemplo base
Definamos una interfaz que represente una criatura:
public interface Criatura {
void alimentar();
}
Una implementación concreta:
public class Tigre implements Criatura {
@Override
public void alimentar() {
System.out.println("El tigre se está alimentando");
}
@Override
public String toString() {
return "Soy un tigre";
}
}
Un contenedor que mantiene una referencia a una criatura:
public class Reserva {
private Criatura criatura;
public Criatura getCriatura() {
return criatura;
}
public void setCriatura(Criatura criatura) {
this.criatura = criatura;
}
@Override
public String toString() {
return criatura != null ? criatura.toString() : "Reserva vacía";
}
}
En la configuración XML clásica, se inyectan las dependencias explícitamente:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tigre" class="com.ejemplo.Tigre" />
<bean id="reserva" class="com.ejemplo.Reserva">
<property name="criatura" ref="tigre" />
</bean>
</beans>
Activación del autoensamblado
El atributo default-autowire en la etiqueta raíz <beans> permite activar el autoensamblado de forma global. Los valores disponibles son:
- no: no se aplica autoensamblado; cada dependencia requiere su etiqueta
<property>. - byName: Spring busca un bean cuyo identificador coincida con el nombre de la propiedad.
- byType: Spring busca un bean cuyo tipo coincida con el tipo de la propiedad.
- constructor: similar a byType, pero la inyección se realiza vía constructor.
Autoensamblado por nombre (byName)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="criatura" class="com.ejemplo.Tigre" />
<bean id="reserva" class="com.ejemplo.Reserva" />
</beans>
Aquí el bean Tigre recibe el identificador criatura, que coincide exactamente con el nombre del atributo en la clase Reserva. Spring detecta esta coincidencia y realiza la inyección automáticamente mediante el método setCriatura.
Autoensamblado por tipo (byType)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="tigre" class="com.ejemplo.Tigre" />
<bean id="reserva" class="com.ejemplo.Reserva" />
</beans>
En este caso, Spring no se basa en el identificador del bean, sino en el tipo. Dado que la propiedad criatura es de tipo Criatura y Tigre implementa dicha interfaz, el contenedor resuelve la dependencia automáticamente.
Conflicto por múltiples candidatos del mismo tipo
Si definimos una segunda implementación de Criatura:
public class Leon implements Criatura {
@Override
public void alimentar() {
System.out.println("El león se está alimentando");
}
@Override
public String toString() {
return "Soy un león";
}
}
Y la registramos junto con Tigre:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="tigre" class="com.ejemplo.Tigre" />
<bean id="leon" class="com.ejemplo.Leon" />
<bean id="reserva" class="com.ejemplo.Reserva" />
</beans>
Spring lanzará una excepción NoUniqueBeanDefinitionException, ya que encuentra dos beans que implementan Criatura (tigre y leon) y no puede decidir cuál inyectar.
Solución 1: Excluir un bean del autoensamblado
El atributo autowire-candidate="false" indica que un bean no participará como candidato en el mecanismo de autoensamblado:
<bean id="tigre" class="com.ejemplo.Tigre" autowire-candidate="false" />
<bean id="leon" class="com.ejemplo.Leon" />
<bean id="reserva" class="com.ejemplo.Reserva" />
De este modo, el único candidato disponible para el tipo Criatura es leon, y Spring lo inyecta sin ambigüedad.
Solución 2: Marcar un bean como prioritario
El atributo primary="true" señala que, entre todos los candidatos del mismo tipo, ese bean debe ser el seleccionado por defecto:
<bean id="tigre" class="com.ejemplo.Tigre" />
<bean id="leon" class="com.ejemplo.Leon" primary="true" />
<bean id="reserva" class="com.ejemplo.Reserva" />
Ambas estrategias resuelven el conflicto, pero desde enfoques opuestos: la primera reduce el conjunto de candidatos y la segunda establece una preferencia explícita.