Springboot使用事件监听
背景
之前遇到了一个需求,在新增完数据以后,需要写入一下这次的操作的信息,比如修改了什么,新增了什么关系之类的。这种需求实现正常来说是构造出来需要记录的信息,然后数据库操作一下写进去就可以了。但是记录的这个操作与核心业务没啥关系,还会导致大量重复的代码(主要是想要整点活)。恰巧之前看到了 spring 内部有事件监听的机制,用此来进行解耦再好不过
我的项目版本为:
参考
首先感谢这篇博文:Spring 解决泛型擦除的思路不错,现在它是我的了。
文章给了莫大的参考,而且非常完整的介绍了从最简单的 demo 开始到最后使用范型来兼容不同也业务的过程,以及过程中的出现的问题和解决的过程以及源码。
此文只是扩展了监听事件时候,如何处理批量数据的情况。推荐先阅读此文后再看本篇文章(可能看完以后就不需要看本文了)。最后再次感谢原博主
使用
单个数据的发布订阅
首先需要一个事件内容类,来封一层表示携带的数据和操作之类的,或者其他需要再订阅时候需要的数据
ResolvableTypeProvider 的详细使用可以看我参考的博文中的介绍
重写 getResolvableType 以适配对于的范型,多个范型需要都实现 ResolvableType.forInstance
import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class EventDTO<T, R> implements ResolvableTypeProvider {
/**
* 操作类型
*/
private T tD;
/**
* 操作内容
*/
private R rD;
@Override
public String toString() {
return JSON.toJSONString(this);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(),
ResolvableType.forInstance(getTD()),
ResolvableType.forInstance(getRD()));
}
}
在需要发布事件的地方如此使用
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import javax.annotation.Resource;
@Slf4j
@Service
public class Service{
// 其余引用
@Resource
private ApplicationContext applicationContext;
// 其余代码
public void method(){
// 其余代码
// tD和rD都表示实际的参数
this.applicationContext.publishEvent(new EventDTO<>(tD, rD));
}
}
然后需要一个监听器来订阅事件,EventDTO 的 T,R 需要替换为你实际使用的类型
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EventListener {
@EventListener
public void handleSingleOperationEvent(EventDTO<T, R> dto) {
// 此处是你监听到数据以后的
log.info("监听到数据咯:{}",dto);
}
}
然后就需要在 EventListener 中来实现具体的业务了
批量数据的发布订阅
实现了单个数据的发布订阅以后,我发现我就遇到了难题:需求需要批量新建数据
那总不能循环新建,然后循环去调用发布订阅吧,这多少有点…………
于是努力了一下,实现了一次订阅的发布后,接受携带 List 的数据
这里有区别的其实是EventDTO有些许不同,需要嵌套泛型类型
import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class BatchEventDTO<T, R extends List<?>> implements ResolvableTypeProvider {
private T tD;
private Class<T> tDClazz;
private R rD;
private Class<?> rDClazz;
@Override
public String toString() {
return JSON.toJSONString(this);
}
@Override
public ResolvableType getResolvableType() {
// 为整个类创建泛型类型
return ResolvableType.forClassWithGenerics(
this.getClass(),
ResolvableType.forClass(operationTypeType),
// 为R创建嵌套的泛型类型
ResolvableType.forClassWithGenerics(List.class,ResolvableType.forClass(listElementType))
);
}
}
订阅和发布时的方式跟单个是一样的
发布
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import javax.annotation.Resource;
@Slf4j
@Service
public class Service{
// 其余引用
@Resource
private ApplicationContext applicationContext;
// 其余代码
public void method(){
// 其余代码
// tD和rD都表示实际的参数,T和R都表示实际的
applicationContext.publishEvent(new CurdOperationRecordBatchEventDTO<>(tD, T.class, listRD, R.class))
}
}
订阅,此处的 T 和 R 是指使用时候实际的类型,注意不要直接年过去
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EventListener {
@EventListener
public void handleSingleOperationEvent(EventDTO<T, List<R>> dto) {
// 此处是你监听到数据以后的
log.info("监听到数据咯:{}",dto);
// 监听到数据后续处理
}
}

浙公网安备 33010602011771号