spring|spring event|Spring内置事件驱动编程模型|类型泛化
优雅处理同类业务
需要为同一类业务(如所有“创建”操作)定义监听器,但又希望根据不同实体类型做区分处理时,泛型事件是很好的解决方案
源码
https://gitee.com/jysemel-spring/spring-boot/tree/master/action/action-event-generic
定义事件类型
package com.jysemel.spring.boot.study.event;
import lombok.Getter;
import lombok.Setter;
// 定义一个泛型事件
@Getter
@Setter
public class EntityCreatedEvent<T> {
private T entity;
}
完整的演示案例
定义事件
- ResolvableTypeProvider
Java的泛型在编译后,其类型参数会被擦除,导致运行时无法获取
package com.jysemel.spring.boot.study.event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GenericEvent<T> implements ResolvableTypeProvider {
private T data;
private String operation;
@Override
public ResolvableType getResolvableType() {
if (this.data != null) {
return ResolvableType.forClassWithGenerics(
getClass(),
ResolvableType.forInstance(this.data)
);
}
return ResolvableType.forClass(getClass());
}
}
定义不同业务类
package com.jysemel.spring.boot.study.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class Order {
private String orderId;
private Double amount;
}
package com.jysemel.spring.boot.study.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
}
监听配置解析
package com.jysemel.spring.boot.study.listener;
import com.jysemel.spring.boot.study.event.GenericEvent;
import com.jysemel.spring.boot.study.event.Order;
import com.jysemel.spring.boot.study.event.Person;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class GenericEventListener {
@PostConstruct
public void init() {
System.out.println("✅✅✅ GenericEventListener 已被 Spring 加载!");
}
/**
* 监听 Person 类型的泛型事件
* Spring 会根据泛型参数自动路由
*/
@Async
@EventListener
public void handlePersonEvent(GenericEvent<Person> event) {
Person person = event.getData();
log.info("【Person监听器】操作: {}, 数据: name={}, age={}", event.getOperation(), person.getName(), person.getAge());
}
/**
* 监听 Order 类型的泛型事件
*/
@Async
@EventListener
public void handleOrderEvent(GenericEvent<Order> event) {
Order order = event.getData();
log.info("【Order监听器】操作: {}, 数据: orderId={}, amount={}", event.getOperation(), order.getOrderId(), order.getAmount());
}
/**
* 监听所有类型的泛型事件(兜底)
* 注意:SpEL 表达式可以精确控制
*/
@Async
@EventListener(condition = "#event.operation == 'DELETE'")
public void handleAllDeleteEvent(GenericEvent<?> event) {
log.info("【通用删除监听器】操作: DELETE, 数据: {}", event.getData());
}
@EventListener
@Async
public void handleRawEvent(GenericEvent event) {
System.out.println("🔥🔥🔥【兜底原始类型】成功捕获事件,数据为: " + event.getData());
}
}
发布事件
package com.jysemel.spring.boot.study.service;
import com.jysemel.spring.boot.study.event.GenericEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class EventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
/**
* 发布泛型事件
* @param data 业务数据(任意类型)
* @param operation 操作类型
* @param <T> 数据类型
*/
public <T> void publish(T data, String operation) {
GenericEvent<T> event = new GenericEvent<>(data, operation);
publisher.publishEvent(event);
System.out.println("事件已发布: " + event);
}
}
验证
- http://localhost:8083/test/person
- http://localhost:8083/test/order
验证结果
✅✅✅ GenericEventListener 已被 Spring 加载!
2026-07-15 09:38:16.252 [main] [] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8083"]
2026-07-15 09:38:16.269 [main] [] INFO org.springframework.boot.tomcat.TomcatWebServer - Tomcat started on port 8083 (http) with context path '/'
2026-07-15 09:38:16.272 [main] [] INFO com.jysemel.spring.boot.Main - Started Main in 1.287 seconds (process running for 1.831)
2026-07-15 09:38:20.001 [http-nio-8083-exec-1] [] INFO org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-07-15 09:38:20.001 [http-nio-8083-exec-1] [] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2026-07-15 09:38:20.003 [http-nio-8083-exec-1] [] INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in 2 ms
2026-07-15 09:38:20.063 [event-async-1] [] INFO com.jysemel.spring.boot.study.listener.GenericEventListener - 【Order监听器】操作: UPDATE, 数据: orderId=ORD-2026-001, amount=299.99
🔥🔥🔥【兜底原始类型】成功捕获事件,数据为: com.jysemel.spring.boot.study.event.Order@7bb383b
事件已发布: GenericEvent(data=com.jysemel.spring.boot.study.event.Order@7bb383b, operation=UPDATE)
🔥🔥🔥【兜底原始类型】成功捕获事件,数据为: com.jysemel.spring.boot.study.event.Person@36cdc7b7
事件已发布: GenericEvent(data=com.jysemel.spring.boot.study.event.Person@36cdc7b7, operation=ADD)
2026-07-15 09:38:25.396 [event-async-4] [] INFO com.jysemel.spring.boot.study.listener.GenericEventListener - 【Person监听器】操作: ADD, 数据: name=张三, age=25
