有个业务场景,业务数据审核通过后需要给用户发短信,发短信过程比较耗时,可能需要几秒甚至十几秒,因此使用异步发短信
使用了注解@Async来实现:
1.SpringApplication启用注解@EnableAsync
@SpringBootApplication @ImportResource(locations = { "classpath:/spring/spring-*.xml" }) @EnableTransactionManagement(proxyTargetClass=true) @EnableScheduling @EnableAutoConfiguration(exclude = { FreeMarkerAutoConfiguration.class }) @EnableSwagger2 @ServletComponentScan(basePackages="com.xx") @EnableMongoRepositories(basePackages = "com.xx.xx.xx.xx") @EnableAsync public class IemsApplication { public static void main(String[] args) { SpringApplication.run(IemsApplication.class, args); } ...
2.在业务层(@Service)具体的审核方法上添加注释@Async
@Async public void cancelAudit(DefectForm defectForm) { Map<String,Object> params = new HashMap<>(); params.put("defectId", defectForm.getDefectId()); //缺陷记录ID params.put("defectStatus", 3); //更新缺陷记录状态审核拒绝 params.put("reason", defectForm.getReason()); //拒绝理由 defectRecordDao.updateByPrimaryKeySelective(params);
//上面是业务处理,下面是发短信 //审核拒绝发送短信,短信发送给缺陷上报人,缺陷内容,审核拒绝理由 Account account = accountDao.findAccountById(xxx); if(account != null && StringUtils.isNotBlank(account.getMobile())){ String mobile = account.getMobile(); String defectContent = defectForm.getDefectContent(); String reason = defectForm.getReason(); Map<String,String> templateData = new HashMap<>(); templateData.put("defectContent", defectContent); templateData.put("reason", reason); smsService.sendSms(null, mobile, SmsConstant.DEFECT_REFUSRD_CODE, templateData,false); logger.debug("缺陷上报记录审核拒绝,发送短信给缺陷记录上报人******"); } }
3.前端逻辑:
/** * 审核拒绝,确定 * @returns */ function makeRefuse(){ var reason = $("#refuse_reason").val(); if (reason==null || reason.trim()==""){ AppUtils.showTooltip("请填写拒绝理由!",false); return; } var curDefect = recordsJson[xxx]; $.ajax({ url: path + '/xxx/xxx/qqq/cancelAudit', type: 'post', dataType: 'json', data:curDefect, success: function(data){ if(data=="ok"){ AppUtils.showTooltip("审核拒绝成功!",true); $("#topForm").attr("action",path + '/xxx/xxx/xxx'); $("#topForm").submit(); } } }); }
4.Controller层
@RequestMapping("/xxx/xxx/cancelAudit") @ResponseBody public String cancelAudit(DefectForm defectForm){ defectRecordService.cancelAudit(defectForm); return "ok"; }
经测试,可以异步更新、发送短信
但是,发现一个严重的问题:前台页面点击取消审核后页面状态偶尔能刷新过来,偶尔还是之前的状态,重新查询一次后,页面显示正常
分析代码:Controller层代码写的有问题,Controller层调用Service层(defectRecordService.cancelAudit(defectForm);),Service层cancelAudit(DefectForm defectForm)方法整个是@Async,
主线程会直接返回,而新启的线程处理Service层的逻辑。这样ajax返回前台,前台再去刷新数据的时候,可能新启线程Service的更新逻辑还没处理完,这样就导致了页面刷新状态错误的问题
其实:我们期望的是,业务逻辑(更新操作)执行完成后再返回;整个业务逻辑(更新操作完成,返回)与发短信异步
修改后的代码:
1.Controller层
@RequestMapping("/xxx/xxx/cancelAudit") @ResponseBody public String cancelAudit(DefectForm defectForm){ defectRecordService.cancelAudit(defectForm); //更新操作,成功后往下走,sendCancelAuditMsg会新启一个线程处理,主线程继续往下走,走到return "ok";返回
//审核拒绝:业务操作完成后发短信 defectRecordService.sendCancelAuditMsg(defectForm); return "ok"; }
2.Service层
//这里我们就不需要添加异步注解了 public void cancelAudit(DefectForm defectForm) { Map<String,Object> params = new HashMap<>(); params.put("defectId", defectForm.getDefectId()); //缺陷记录ID params.put("defectStatus", 3); //更新缺陷记录状态审核拒绝 params.put("reason", defectForm.getReason()); //拒绝理由 defectRecordDao.updateByPrimaryKeySelective(params); }
//把发短信的逻辑抽出来,单独一个方法,使用异步注解
@Async
public void sendCancelAuditMsg(DefectForm defectForm){
//审核拒绝发送短信,短信发送给缺陷上报人,缺陷内容,审核拒绝理由
Account account = accountDao.findAccountById(defectForm.getCreatorUserid());
if(account != null && StringUtils.isNotBlank(account.getMobile())){
String mobile = account.getMobile();
String defectContent = defectForm.getDefectContent();
String reason = defectForm.getReason();
Map<String,String> templateData = new HashMap<>();
templateData.put("defectContent", defectContent);
templateData.put("reason", reason);
smsService.sendSms(null, mobile, SmsConstant.DEFECT_REFUSRD_CODE, templateData,false);
logger.debug("缺陷上报记录审核拒绝,发送短信给缺陷记录上报人******");
}
}
至此问题就解决了,写博客标注一下