如何利用spring自带的事件发布订阅实现各种异步操作

许久不见,小伙伴们!

最近工作确实非常忙碌,今天终于抽空整理了一篇关于如何在实际开发中利用Spring框架自带的事件机制实现异步操作的文章。希望这篇分享能够给大家带来一些启发。

首先,我写这个的原因是因为我实际开发中遇到这么一个开发场景:

我们的一个新项目登录相关的是用的keycloak,这也就说明 我们的用户是存在于keycloak中,我接到的需求是 批量导入用户功能,这也就意味着 我要找到keycloak的批量新增用户接口,但很不幸的时keycloak并没有批量新增用户的接口只有一个新增用户的接口

那我要批量新增怎么办,只能解析完excel数据UserList然后循环去调这个接口,这个涉及到调用第三方接口还是循环调就考虑异步去调接口了.

废话不多说,直接上代码!!

实现步骤

我先把controller和service代码贴一下 有头有尾看起来比较顺畅
Controller

java 复制代码
    @PostMapping("/action/import")
    @ApiOperation(value = "导入用户", notes = "导入用户")
    public void importFile(@RequestParam MultipartFile file) throws IOException {
        userService.importFile(file);
    }

service

java 复制代码
  @Autowired
    private CreatorUserPublisher creatorUserPublisher;

    public void importFile(MultipartFile file) throws IOException {
        ExcelUtils.importFile(file, UserImportDTO.class, new ImportHandler<>(list -> createAll(list)));
    }

 public void createAll(List<UserImportDTO> list) {
        String eventId = IdUtil.randomUUID();
        OperationLogUtil.setEventId(eventId);
        // 判断数据是否有重复
        dataHandle(list);
        // 组装用户数据
        List<UserParam> userParams = new ArrayList<>();
        for (UserImportDTO userImportDTO : list) {
            UserParam userParam = new UserParam();
            userParam.setUsername(userImportDTO.getUsername());
            userParam.setEmail(userImportDTO.getEmail());
            userParam.setEnabled(true);

            List<UserParam.CredentialsDTO> credentials = new ArrayList<>();
            UserParam.CredentialsDTO credentialsDTO = new UserParam.CredentialsDTO();
            credentialsDTO.setType("password");
            credentialsDTO.setValue(userImportDTO.getP6d());
            credentials.add(credentialsDTO);
            userParam.setCredentials(credentials);

            UserParam.AttributesDTO attributesDTO = new UserParam.AttributesDTO();
            attributesDTO.setProject(userImportDTO.getUsername());
            userParam.setAttributes(attributesDTO);
            userParams.add(userParam);
        }
        // 异步调用keycloak接口新增用户

        Map<String, Object> map = new HashMap<>();
        map.put("userParams", userParams);
        map.put("eventId", eventId);
        log.info("发布异步新增keycloak用户任务");
        creatorUserPublisher.creatorUserList(map);
    }

以上代码大部分都不重要

下面我来讲一下这个service里面注入了一个
@Autowired
private CreatorUserPublisher creatorUserPublisher;

这个东西的作用就是发布事件

接下来进入正题
1.创建事件类

java 复制代码
public class CreatorUserEvent extends ApplicationEvent {
    private static final long serialVersionUID = -1843750195817873742L;

    private Map<String, Object> map;

    public CreatorUserEvent(Object source) {
        super(source);
    }

    public CreatorUserEvent(Object source, Map<String, Object> map) {
        super(source);
        this.map = map;
    }

    public Map<String, Object> getMap() {
        return map;
    }
}

2.发布事件

这个就是我们在service里面注入之后发布的创建用户的事件

java 复制代码
@Service
@Slf4j
public class CreatorUserPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void creatorUserList(Map<String, Object> map) {
        applicationEventPublisher.publishEvent(new CreatorUserEvent(this, map));
    }
}

3.监听事件

这个就是处理我们之间在service里面发布的创建用户的事件

java 复制代码
@Service
@Slf4j
public class CreatorUserListener implements ApplicationListener<CreatorUserEvent> {

    @Autowired
    private UserService userService;

    @Override
    public void onApplicationEvent(CreatorUserEvent creatorUserEvent) {
        log.info("开始执行异步任务");
        Map<String, Object> map = creatorUserEvent.getMap();

        List<UserParam> userParams = (List<UserParam>) map.get("userParams");
        String eventId = (String) map.get("eventId");
        for (UserParam userParam : userParams) {
            if (!userService.addUser(userParam)) {
                log.error("新增用户失败:{}", userParam.getUsername());
                throw new SecurityResourceUnavailableException(UserError.ADD_USER_ERROR);
            }
        }
    }
}

到这就完活了 是不是很简单 就三步: 创建事件、发布事件、监听事件

只要service里面的方法执行完之后就会给前端返回响应消息了,后面的事件监听到之后处理的逻辑跟返回前端没关了也不会影响接口超时,如果监听事件处理的逻辑得结果有需要返回前端的话,建议用websocket去处理吧 主动向前端去发送处理结果

希望对你们有帮助。 开心写代码☺

相关推荐
小灰灰__14 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭18 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果38 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林44 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
许野平1 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
duration~2 小时前
Maven随笔
java·maven
zmgst2 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD2 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构