许久不见,小伙伴们!
最近工作确实非常忙碌,今天终于抽空整理了一篇关于如何在实际开发中利用Spring框架自带的事件机制实现异步操作的文章。希望这篇分享能够给大家带来一些启发。
首先,我写这个的原因是因为我实际开发中遇到这么一个开发场景:
我们的一个新项目登录相关的是用的keycloak,这也就说明 我们的用户是存在于keycloak中,我接到的需求是 批量导入用户功能,这也就意味着 我要找到keycloak的批量新增用户接口,但很不幸的时keycloak并没有批量新增用户的接口只有一个新增用户的接口
那我要批量新增怎么办,只能解析完excel数据UserList然后循环去调这个接口,这个涉及到调用第三方接口还是循环调就考虑异步去调接口了.
废话不多说,直接上代码!!
实现步骤
我先把controller和service代码贴一下 有头有尾看起来比较顺畅
Controller
@PostMapping("/action/import")
@ApiOperation(value = "导入用户", notes = "导入用户")
public void importFile(@RequestParam MultipartFile file) throws IOException {
userService.importFile(file);
}
service
@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.创建事件类
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里面注入之后发布的创建用户的事件
@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里面发布的创建用户的事件
@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去处理吧 主动向前端去发送处理结果
希望对你们有帮助。 开心写代码☺