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

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

相关推荐
小旋风-java11 分钟前
springboot整合dwr
java·spring boot·后端·dwr
Pandaconda16 分钟前
【计算机网络 - 基础问题】每日 3 题(二十六)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展
虽千万人 吾往矣17 分钟前
golang strings api接口
开发语言·后端·golang
JAVA坚守者17 分钟前
Maven常见解决方案
java·maven
景天科技苑20 分钟前
【Go语言】深入解读Go语言中的指针,助你拨开迷雾见月明
开发语言·后端·golang·指针·go指针·go语言指针
虽千万人 吾往矣20 分钟前
golang格式化输入输出
开发语言·后端·golang
聊天宝快捷回复28 分钟前
必收藏,售后客服日常回复必备的话术 (精华版)
java·前端·数据库·经验分享·微信·职场发展·快捷回复
wanyuanshi30 分钟前
map的键排序方法
java·数据结构·算法
热爱前端的小wen33 分钟前
maven的介绍与安装
java·spring·maven·springboot
听潮阁35 分钟前
【SpringBoot详细教程】-05-整合Druid操作数据库【持续更新】
数据库·spring boot·后端