Spring provee un mecanismo de eventos que permite la comunicación entre beans del contenedor. Cuando un bean completa una tarea y necesita que otro bean continúe el procesamiento, se puede utilizar el sistema de eventos de Spring para lograrlo.
Pasos para usar eventos en Spring
- Crear un evento personalizado heredando de
ApplicationEvent - Definir un listener implementando
ApplicationListener - Publicar el evento usando el contenedor de Spring
1. Definir un evento
public class PedidoEvent extends ApplicationEvent {
private String cliente;
private String accion;
public PedidoEvent(Object source) {
super(source);
}
public PedidoEvent(Object source, String cliente, String accion) {
super(source);
this.cliente = cliente;
this.accion = accion;
}
public String getCliente() {
return cliente;
}
public void setCliente(String cliente) {
this.cliente = cliente;
}
public String getAccion() {
return accion;
}
public void setAccion(String accion) {
this.accion = accion;
}
}
2. Crear el listener
@Component
public class PedidoListener implements ApplicationListener<PedidoEvent> {
@Async
@Override
public void onApplicationEvent(PedidoEvent evento) {
System.out.println("Cliente: " + evento.getCliente() +
" - Acción: " + evento.getAccion());
}
}
3. Publicar el evento
@Component
public class PublicadorEvento {
@Autowired
private ApplicationContext applicationContext;
public void publicar(String cliente, String accion) {
applicationContext.publishEvent(
new PedidoEvent(this, cliente, accion));
}
}
4. Configuración y prueba
@Configuration
@ComponentScan("com.ejemplo.eventos")
public class ConfigEvento {
}
public class Aplicacion {
private static AnnotationConfigApplicationContext contexto;
public static void main(String[] args) {
iniciar();
}
private static void iniciar() {
if (contexto == null) {
contexto = new AnnotationConfigApplicationContext(
ConfigEvento.class);
}
contexto.getBean(PublicadorEvento.class)
.publicar("Juan Perez", "Devolución solicitada");
}
}
Salida esperada: Cliente: Juan Perez - Acción: Devolución solicitada
¿Eventos de Application vs TaskExecutor?
Surge la pregunta: ¿qué diferencia hay entre usar eventos de Application y simplemente ejecutar tareas con un TaskExecutor? Ambos permiten ejecución asíncrona, pero el enfoque es distinto.
Ejemplo con AsyncExecutor:
@Configuration
@EnableAsync
public class ConfigAsync implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.initialize();
return executor;
}
}
// Uso
@Autowired
private AsyncExecutor asyncExecutor;
Executor executor = asyncExecutor.getAsyncExecutor();
executor.execute(() -> {
// Lógica del negocio
});
Ejemplo con TaskExecutor vía XML:
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="3" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
<property name="keepAliveSeconds" value="300" />
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
@Resource
private TaskExecutor taskExecutor;
taskExecutor.execute(() -> {
// Lógica del negocio
});
La diferencia fundamental es que ApplicationEvent implementa el patrón observador, reduciendo el acoplamiento entre el publicader y el suscriptor. Aunque ambas alternativas pueden usar @Async para asincronía, los eventos siguen un diseño orientado a objetos más desacoplado, mientras que TaskExecutor simplemente ejecuta tareas en un hilo separado.
¿Observador vs Publicación/Suscripción (Pub/Sub)?
Esto lleva a otra pregunta: ¿es el patrón observador equivalente al modelo Pub/Sub usado en sistemas de mensajería (MQ)? La respuesta es que son similares pero no idénticos.
Diferencias clave:
- En el patrón observador, el sujeto (publicador) conoce directamente a los observadores (suscriptores) y los notifica.
- En Pub/Sub, existe un canal o broker intermedio que desacopla completamente al publicador del suscriptor.
- El patrón observador opera dentro del mismo proceso, mientras que Pub/Sub suele ser cross-process (ej. RabbitMQ, Kafka).
Se puede decir que el patrón observador es una implementación más simple y acoplada, mientras que Pub/Sub representa una evolución más flexible y desacoplada, adecuada para sistemas distribuidos.