如何利用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去处理吧 主动向前端去发送处理结果

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

相关推荐
陈随易2 小时前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·后端·程序员
IT_陈寒5 小时前
Vite的热更新突然不香了,排查三小时差点砸键盘
前端·人工智能·后端
子兮曰5 小时前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
用户8356290780516 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
小满zs6 小时前
Go语言第二章(小无相功)
后端·go
用户8356290780516 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
karry_k6 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
妙码生花6 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
贰先生6 小时前
Xiuno BBS X版 用户封禁系统
后端
karry_k6 小时前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端