1. Repaso de Spring Beans
Un Spring Bean es, en esencia, un objeto que es gestionado por el contenedor IoC de Spring. Esos objetos son creados, inicializados, configurados y también destruidos por el contenedor según la configuración de la aplicación. Si quieres que tu objeto Java forme parte de la magia de Spring, debe estar registrado como Bean.
Imagina a un encargado en un almacén que siempre sabe dónde está la herramienta que necesitas (o el objeto). Ese encargado es el contenedor IoC, y las herramientas en el almacén son nuestros Beans.
¿Cómo se registran los Beans en Spring?
Hay varias formas de registrar Beans:
- Anotaciones (por ejemplo,
@Component,@Service,@Repository). - Configuración basada en Java usando
@Configurationy@Bean. - Configuración XML (sí, todavía existe, pero se usa menos, ¿para qué cuando hay anotaciones en Java?).
Por eso Spring es flexible: elige la forma que te resulte más cómoda para registrar Beans.
2. Ciclo de vida de un Spring Bean
Los Beans, como los seres vivos, tienen un ciclo de vida. Este camino incluye las etapas de creación, inicialización, uso y destrucción — todo esto lo controla el contenedor IoC. Vamos a ver cómo funciona.
Etapas principales del ciclo de vida
- Creación: el Bean es creado por el contenedor IoC.
- Inicialización: en este momento el Bean puede configurar su estado interno.
- Uso: el Bean comienza a hacer su trabajo (por ejemplo, procesar peticiones).
- Destrucción: cuando el Bean ya no es necesario, el contenedor lo destruye (si no es, por ejemplo, un
singleton).
Visualización del ciclo de vida
1. Creación del objeto
↓
2. Inyección de dependencias (DI)
↓
3. Llamada a métodos de inicialización (@PostConstruct)
↓
4. Uso del Bean
↓
5. Limpieza de recursos (@PreDestroy)
Ahora vamos a ver un ejemplo concreto.
3. ¿Cómo gestiona Spring el ciclo de vida?
Spring ofrece anotaciones muy cómodas para esto. Conozcamos las principales.
Anotaciones @PostConstruct y @PreDestroy
@PostConstruct: ejecuta acciones después de la creación del Bean y de la inyección de todas sus dependencias (por ejemplo, conectar un servicio o cargar datos).@PreDestroy: se dispara antes de la destrucción del Bean. Se usa para liberar recursos (por ejemplo, cerrar conexiones).
Ejemplo:
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class LifecycleDemoBean {
@PostConstruct
public void init() {
System.out.println("¡Bean inicializado!");
}
public void doSomething() {
System.out.println("¡Bean en funcionamiento!");
}
@PreDestroy
public void cleanup() {
System.out.println("¡Bean destruido!");
}
}
Cuando Spring detecta las anotaciones @PostConstruct y @PreDestroy, garantiza que esos métodos se llamen en los momentos correspondientes.
Alternativa con las interfaces InitializingBean y DisposableBean
Si prefieres interfaces en lugar de anotaciones (por ejemplo, para un contrato más explícito), puedes usar:
InitializingBean— el métodoafterPropertiesSet()se llama después de crear el Bean.DisposableBean— el métododestroy()se llama antes de destruir el Bean.
Ejemplo:
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class AlternativeLifecycleBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("¡Objeto configurado (InitializingBean)!");
}
@Override
public void destroy() throws Exception {
System.out.println("¡Objeto destruido (DisposableBean)!");
}
}
Aunque este enfoque funciona, en proyectos modernos se usan más las anotaciones @PostConstruct y @PreDestroy por su simplicidad y legibilidad.
4. Gestión de Beans con anotaciones
@Component y compañía
@Component: anotación general para cualquier Bean, la usas si no encaja una anotación más específica.@Service: para clases con lógica de negocio.@Repository: para clases que manejan el acceso a datos.@Controller: para controladores MVC.
Ejemplo de uso de @Component:
@Component
public class SimpleBean {
public void sayHello() {
System.out.println("¡Hola, mundo de Beans!");
}
}
@Autowired y la inyección automática de dependencias
Ya hemos hablado un poco sobre inyección de dependencias, pero profundicemos.
@Autowired: permite que Spring configure automáticamente las dependencias.
Ejemplo con @Autowired:
@Component
public class DependencyBean {
public String getDependencyInfo() {
return "Información sobre la dependencia.";
}
}
@Component
public class AutowiredBean {
private final DependencyBean dependency;
@Autowired
public AutowiredBean(DependencyBean dependency) {
this.dependency = dependency;
}
public void printDependencyInfo() {
System.out.println(dependency.getDependencyInfo());
}
}
Al arrancar, Spring encontrará DependencyBean y se lo pasará a AutowiredBean.
5. Scopes de los Beans
Por defecto todos los Beans en Spring tienen el scope singleton. Eso significa que solo existe una instancia del Bean en el contenedor IoC. Pero a veces necesitamos controlar los scopes.
Scopes principales
- Singleton: un Bean por todo el contenedor IoC.
- Prototype: se crea un nuevo objeto cada vez que se solicita el Bean.
- Request: una instancia por cada petición HTTP.
- Session: una instancia por cada sesión HTTP.
Configurar el scope
La anotación @Scope te permite controlar el ciclo de vida del Bean.
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class PrototypeBean {
public PrototypeBean() {
System.out.println("¡Se ha creado una nueva instancia de PrototypeBean!");
}
}
Cada vez que uses este Bean se creará una nueva instancia.
6. Errores típicos y cómo solucionarlos
- Error por dependencia ausente: si usas
@Autowiredpero el Bean no está registrado, Spring lanzaráNoSuchBeanDefinitionException. ¿Solución? Asegúrate de que el Bean esté registrado mediante anotación o configuración. - Destrucción de Beans con scope prototype: Spring no gestiona el ciclo de vida de los Beans con scope
prototype, por lo que debes destruirlos tú mismo. - Coincidencia múltiple de Beans: si tienes varios Beans del mismo tipo, Spring puede confundirse. Usa
@Qualifierpara indicar el Bean concreto.
Ejemplo de @Qualifier:
@Component("catBean")
public class Cat {}
@Component("dogBean")
public class Dog {}
@Component
public class AnimalShelter {
@Autowired
@Qualifier("catBean") // Indicamos qué Bean usar
private Cat pet;
}
¿De dónde sacamos todo esto?
Si quieres profundizar, aquí tienes un par de enlaces útiles:
¡Vivan los Beans, el ciclo de vida y lo que nos hace la vida más fácil! El siguiente paso — aún más magia de Spring.
GO TO FULL VERSION