Gestión del Ciclo de Vida y Configuración de Beans en Spring
El contenedor de Spring implementa los principios de Inversión de Control (IoC) y Desacoplamiento mediante Inyección de Dependencias (DI). Estos mecanismos trasladan la responsabilidad de crear y administrar objetos (componentes) de la aplicación al framework. Los objetos gestionados por el contenedor de Spring se denominan Beans. Un Bean es una instancia de Java que se registra en el contenedor de Spring y cuyo ciclo de vida, configuración y dependencias son administrados por este.
Contenedores IoC en Spring
El contenedor de Spring es el núcleo que administra los Beans. Las interfaces principales para interactuar con el contenedor son:
BeanFactory: Interfaz raíz que proporciona la funcionalidad básica de contenedor IoC.ApplicationContext: Interfaz que hereda deBeanFactoryy añade características más ricas como la internacionalización, acceso a recursos y la publicación de eventos. Es la interfaz más utilizada en aplicaciones reales.
Una diferencia clave es la estrategia de inicialización: BeanFactory inicializa Beans de forma perezosa (al momento de la primera solicitud), mientras que ApplicationContext pre-inicializa los Beans de ámbito singleton al arrancar el contenedor, lo que puede mejorar el rendimiento.
Configuración de Beans mediante XML
La configuración clásica de Spring utiliza archivos XML. La raíz es el elemento <beans>, y cada Bean se define con un elemento <bean> que especifica, como mínimo, un id único y la clase class.
Configuración del Ámbito (Scope): Por defecto, un Bean es singleton (una única instancia en el contenedor). Se puede cambiar a prototype (una nueva instancia por cada solicitud).
<bean id="servicioPrimario" class="com.ejemplo.ServicioPrimario" scope="singleton"/>
<bean id="servicioPrototype" class="com.ejemplo.ServicioPrototype" scope="prototype"/>
</div>Instanciación de Beans
----------------------
### 1. Mediante Constructor (por defecto)
Spring utiliza por defecto el constructor sin argumentos de la clase del Bean.
<div class="code-block"> ```
public class MiComponente {
public MiComponente() {
System.out.println("Constructor ejecutado");
}
}
<bean id="miComponente" class="com.ejemplo.MiComponente"/>
</div>### 2. Mediante Fábrica Estática
Se invoca un método estático de una fábrica para obtener la instancia del Bean. El atributo `class` apunta a la fábrica, y `factory-method` al nombre del método.
<div class="code-block"> ```
public class FabricaComponenteA {
public static ComponenteA crear() {
// Lógica compleja o acceso a API externa (ej. MyBatis Mapper)
return new ComponenteA();
}
}
<bean id="componenteA" class="com.ejemplo.FabricaComponenteA" factory-method="crear"/>
</div>### 3. Mediante Fábrica de Instancia
Se invoca un método no estático de una instancia (Bean) ya creada.
<div class="code-block"> ```
public class FabricaComponenteB {
public ComponenteB producir() {
return new ComponenteB();
}
}
<!-- 1. Definir el Bean de la fábrica -->
<bean id="fabricaB" class="com.ejemplo.FabricaComponenteB"/>
<!-- 2. Definir el Bean objetivo usando la fábrica -->
<bean id="componenteB" factory-bean="fabricaB" factory-method="producir"/>
</div>Spring también ofrece una interfaz `FactoryBean` para simplificar este proceso, donde la fábrica misma es el Bean definido.
Ensamblaje (Wiring) e Inyección de Dependencias
-----------------------------------------------
### Ensamblaje mediante XML
Existen dos formas principales de inyectar dependencias en los Beans configurados en XML:
1. **Inyección por Setter:** Requiere que la clase tenga un constructor sin argumentos y métodos setter para las propiedades.
2. **Inyección por Constructor:** Las dependencias se pasan como argumentos del cosntructor.
Spring determina la estrategia de ensamblado automático (*autowiring*) a través del atributo `autowire` en el elemento `<bean>` (por nombre, por tipo, etc.).
### Ensamblaje mediante Anotaciones (Recomendado)
Las anotaciones simplifican enormemente la configuración. Es necesario habilitar el escaneo de componentes en el XML.
<div class="code-block"> ```
<context:component-scan base-package="com.ejemplo"/>
| Anotación | Propósito |
|---|---|
@Component |
Marcar una clase como un Bean genérico. |
@Service, @Repository, @Controller |
Especializaciones de @Component con semántica más clara (capa de servicio, persistencia, presentación). |
@Autowired |
Inyección automática de dependencias (por tipo). |
@Resource |
Inyección por nombre (JSR-250). |
@Value |
Inyección de valores literales o desde propiedades. |
@Scope |
Definir el ámbito del Bean. |
Ciclo de Vida de un Bean
Spring administra el ciclo de vida completo de los Beans singleton, pero solo la creación de los Beans prototype. Las fases son:
1. Creación
singleton: Se crea al iniciar el contenedor (o de forma perezosa conlazy-init="true").prototype: Se crea en cada petición (context.getBean()).
2. Inicialización
Se ejecuta después de que las dependencias hayan sido inyectadas. Se puede definir de tres maneras:
- Enterfaz
InitializingBean: Implementar el métodoafterPropertiesSet(). - Método personalizado en XML: Usar el atributo
init-method. - Anotación
@PostConstruct: (JSR-250) Método estándar de Java.
3. Destrucción
Se ejecuta antes de que el contenedor destruya el Bean (solo para singleton). Formas de definirlo:
- Interfaz
DisposableBean: Implementar el métododestroy(). - Método personalizado en XML: Usar el atributo
destroy-method. - Anotación
@PreDestroy: (JSR-250) Método estándar de Java.
Ejemplo completo con anotaciones:
@Component
@Scope("singleton")
public class MotorServicio {
public MotorServicio() {
System.out.println("1. Constructor: MotorServicio creado");
}
@Autowired
private RepositorioDatos repositorio; // Inyección automática
@Value("${app.nombre}")
private String nombreApp;
@PostConstruct
public void inicializar() {
System.out.println("2. Inicialización: Motor arrancado. App: " + nombreApp);
repositorio.conectar();
}
public String procesar() {
return repositorio.obtenerDatos();
}
@PreDestroy
public void detener() {
System.out.println("3. Destrucción: Liberando recursos del motor");
repositorio.desconectar();
}
}
</div><div class="code-block"> ```
// Prueba del ciclo de vida
public class PruebaCicloVida {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MotorServicio motor = ctx.getBean(MotorServicio.class);
motor.procesar();
ctx.close(); // Dispara el método @PreDestroy
}
}