SpringBoot @Async 異步處理業務邏輯和發短信邏輯
阿新 • • 發佈:2018-08-28
場景 ems agg tor ctf sele attr cati nag
有個業務場景,業務數據審核通過後需要給用戶發短信,發短信過程比較耗時,可能需要幾秒甚至十幾秒,因此使用異步發短信
使用了註解@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("缺陷上報記錄審核拒絕,發送短信給缺陷記錄上報人******");
}
}
至此問題就解決了,寫博客標註一下
SpringBoot @Async 異步處理業務邏輯和發短信邏輯