標籤:使用者 data sap imp ant spring cat 理由 logger
有個業務情境,業務資料審核通過後需要給使用者發簡訊,發簡訊過程比較耗時,可能需要幾秒甚至十幾秒,因此使用非同步發簡訊
使用了註解@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")@EnableAsyncpublic class IemsApplication {public static void main(String[] args) {SpringApplication.run(IemsApplication.class, args);} ...
2.在業務層(@Service)具體的審核方法上添加註釋@Async
@Asyncpublic void cancelAudit(DefectForm defectForm) {Map<String,Object> params = new HashMap<>();params.put("defectId", defectForm.getDefectId()); //缺陷記錄IDparams.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")@ResponseBodypublic 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")@ResponseBodypublic 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()); //缺陷記錄IDparams.put("defectStatus", 3); //更新缺陷選項組審核拒絕params.put("reason", defectForm.getReason()); //拒絕理由defectRecordDao.updateByPrimaryKeySelective(params);}
//把發簡訊的邏輯抽出來,單獨一個方法,使用非同步註解@Asyncpublic 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 非同步處理商務邏輯和發簡訊邏輯