Springboot使用事件监听

背景

之前遇到了一个需求,在新增完数据以后,需要写入一下这次的操作的信息,比如修改了什么,新增了什么关系之类的。这种需求实现正常来说是构造出来需要记录的信息,然后数据库操作一下写进去就可以了。但是记录的这个操作与核心业务没啥关系,还会导致大量重复的代码(主要是想要整点活)。恰巧之前看到了 spring 内部有事件监听的机制,用此来进行解耦再好不过

我的项目版本为:Static Badge Static Badge

参考

首先感谢这篇博文: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);
        // 监听到数据后续处理
    }

}

posted @ 2025-04-30 17:18  忆故人  阅读(103)  评论(0)    收藏  举报