Pollito Blog
November 25, 2024

Hablemos de Java: Beans

Posted on November 25, 2024  •  8 minutes  • 1651 words  • Other languages:  English

Bean… Es uno de esos términos que los desarrolladores de Java usamos tanto que a veces nos olvidamos de detenernos y apreciar su elegancia. Descubramos qué es realmente un bean de Spring.

¿Qué es un Bean?

En el marco de Spring, un bean es simplemente un objeto que administra el contenedor IoC (Inversión de control) de Spring. Es la columna vertebral del mecanismo de inyección de dependencias (DI) de Spring.

Para desglosarlo:

Características clave de un Spring Bean

1. Definido en el contexto

2. Singleton por defecto

3. Inyección de dependencia

4. Gestión del ciclo de vida

Ejemplo

Aquí hay un bean simple:

@Component
public class CoffeeMaker {
    public String brew() {
        return "Brewing a fresh cup of Java!";
    }
}

Y otro bean que depende de él:

@Service
public class CoffeeShop {
    private final CoffeeMaker coffeeMaker;

    public CoffeeShop(CoffeeMaker coffeeMaker) {
        this.coffeeMaker = coffeeMaker;
    }

    public void openShop() {
        System.out.println(coffeeMaker.brew());
    }
}

El contenedor de Spring detecta las anotaciones @Component y @Service, crea beans para estas clases y las conecta.

Inyección de dependencia para conectar beans

La inyección de dependencias (DI) es un patrón de diseño en el que las dependencias de un objeto las proporciona una fuente externa (el contenedor IoC de Spring) en lugar de que el propio objeto las cree.

Este es el mejor vídeo que existe que explica el tema:

En Spring, la DI se utiliza para conectar los beans, lo que hace que las aplicaciones estén acopladas de forma flexible y sean más fáciles de testear y mantener.

Métodos de inyección

1. Inyección de campo: Utilice @Autowired directamente en un campo.

@Autowired
private MyService myService;

2. Inyección de constructor (preferido): Las dependencias se proporcionan a través del constructor.

@Service
public class MyService {
    private final MyDependency dependency;

    public MyService(MyDependency dependency) {
        this.dependency = dependency;
    }
}

3. Inyección de setter: Las dependencias se inyectan a través de un método setter.

private MyDependency dependency;

@Autowired
public void setDependency(MyDependency dependency) {
    this.dependency = dependency;
}

¿Por qué utilizar la inyección de dependencia?

Generalmente se prefiere la inyección de constructor porque garantiza que las dependencias sean inmutables y obligatorias para el funcionamiento del objeto.

¿Por qué Singleton por defecto?

1. Eficiencia

2. Consistencia

3. Inyección de dependencia

4. Thread-Safety

¿Cuándo querrías cambiar el comportamiento de Singleton?

El modelo singleton no siempre es adecuado. A continuación, se indican algunas situaciones en las que tiene sentido modificarlo:

1. Beans con estado (por ejemplo, con ámbito de solicitud/sesión)

@Component
@RequestScope
public class ShoppingCart {
private final List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    public List<Item> getItems() {
        return items;
    }
}

2. Ámbito de prototipo para comportamiento por instancia

@Component
@Scope("prototype")
public class TemporaryWorker {
    private final String id = UUID.randomUUID().toString();

    public String getId() {
        return id;
    }
}

¿Cuándo seguir con los Singleton?

Spring utiliza singleton de manera predeterminada porque se adapta al caso de uso más común: servicios compartidos sin estado. Debe cambiar el alcance cuando el estado o la duración se vuelven críticos para la función del bean (por ejemplo, comportamiento por solicitud/sesión, datos específicos de la tarea).

Si se pregunta si necesita un no singleton, siempre pregunte:

El ciclo de vida de un Spring Bean

1. Instanciación

2. Inyección de dependencias

3. Hooks posteriores a la inicialización

4. Listo para usar

5. Destrucción

Hooks de ciclo de vida en detalle

1. Hooks de inicialización

@Component
public class ExampleBean {
    @PostConstruct
    public void initialize() {
        System.out.println("Bean is initialized!");
    }
}

2. Hooks de destrucción

@Component
public class ExampleBean {
    @PreDestroy
    public void cleanup() {
        System.out.println("Bean is being destroyed!");
    }
}

Ciclo de vida completo en código

@Component
public class ExampleBean implements InitializingBean, DisposableBean {
    public ExampleBean() {
        System.out.println("1. Bean is instantiated.");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("2. @PostConstruct: Bean is initialized.");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("3. afterPropertiesSet(): Custom initialization logic.");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("4. @PreDestroy: Cleanup before destruction.");
    }

    @Override
    public void destroy() {
        System.out.println("5. destroy(): Final cleanup.");
    }
}

Output:

1. Bean is instantiated.
2. @PostConstruct: Bean is initialized.
3. afterPropertiesSet(): Custom initialization logic.
4. @PreDestroy: Cleanup before destruction.
5. destroy(): Final cleanup.

Ciclo de vida de los beans con scopes

¿Cuándo utilizarías estos hooks?

Inicialización

Destrucción:

Spring le brinda un control detallado sobre el ciclo de vida de un bean. Si bien los ganchos como @PostConstruct y @PreDestroy son los más modernos y ampliamente utilizados, el ciclo de vida es lo suficientemente flexible como para adaptarse a la lógica personalizada según sea necesario.

Conclusión

Hey, check me out!

You can find me here