springBoot中異步調用
上傳文件,同時生成預覽地址,順序執行比較慢,考慮用異步調用,文件上傳成功後返回頁面,並異步調用接口生成預覽地址。
1、介紹
異步請求的處理。除了異步請求,一般上我們用的比較多的應該是異步調用。通常在開發過程中,會遇到一個方法是和實際業務無關的,沒有緊密性的。比如記錄日誌信息等業務。這個時候正常就是啓一個新線程去做一些業務處理,讓主線程異步的執行其他業務。
2、使用方式(基於spring下)
- 需要在啓動類加入@EnableAsync使異步調用@Async註解生效
- 在需要異步執行的方法上加入此註解即可@Async,默認的線程池中執行
注意事項:
-
3、注意事項
-
調用的異步方法,不能爲同一個類的方法(包括同一個類的內部類),簡單來說,因爲Spring在啓動掃描時會爲其創建一個代理類,而同類調用時,還是調用本身的代理類的,所以和平常調用是一樣的。其他的註解如@Cache等也是一樣的道理,說白了,就是Spring的代理機製造成的。所以在開發中,最好把異步服務單獨抽出一個類來管理。下面會重點講述。。
4、什麼情況下會導致@Async異步方法會失效?
- 調用同一個類下注有@Async異步方法:在spring中像@Async和@Transactional、cache等註解本質使用的是動態代理,其實Spring容器在初始化的時候Spring容器會將含有AOP註解的類對象“替換”爲代理對象(簡單這麼理解),那麼註解失效的原因就很明顯了,就是因爲調用方法的是對象本身而不是代理對象,因爲沒有經過Spring容器,那麼解決方法也會沿着這個思路來解決。
- 調用的是靜態(static )方法
-
調用(private)私有化方法
5、解決4中問題1的方式(其它2,3兩個問題自己注意下就可以了)
- 將要異步執行的方法單獨抽取成一個類,原理就是當你把執行異步的方法單獨抽取成一個類的時候,這個類肯定是被Spring管理的,其他Spring組件需要調用的時候肯定會注入進去,這時候實際上注入進去的就是代理類了。
-
其實我們的注入對象都是從Spring容器中給當前Spring組件進行成員變量的賦值,由於某些類使用了AOP註解,那麼實際上在Spring容器中實際存在的是它的代理對象。那麼我們就可以通過上下文獲取自己的代理對象調用異步方法。
3.上代碼
- 啓動類加入@EnableAsync
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@MapperScan("com.chinacoal.microservice.impl.mapper")
public class AttachmentApplication {
public static void main(String[] args) {
SpringApplication.run(AttachmentApplication.class, args);
}
}
- controller
package com.chinacoal.microservice.impl.controller;
import com.chinacoal.microservice.impl.service.impl.SyncAttfileServiceImpl;
import com.chinacoal.microservice.model.attachment.CcmsAttList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author sc
* @createTime 2019/12/18 10:30
* @description
*/
@RestController
public class SyncController {
@Autowired
private SyncAttfileServiceImpl syncServiceImpl;
/**
* 順序 調用
* @return
*/
@GetMapping(value = "/test1")
public String test1(){
System.out.println("111111111");
String test1 = syncServiceImpl.getTest1();
System.out.println(test1);
System.out.println("2222222");
return "success";
}
/**
* 異步 調用
* @return
*/
@GetMapping(value = "/test2")
public String test2(){
System.out.println("111111111");
syncServiceImpl.getTest2();
System.out.println("2222222");
return "success";
}
/**
* 異步 調用 業務邏輯測試
* @return
*/
@GetMapping(value = "/test3")
public String test3(){
System.out.println("111111111");
List<CcmsAttList> list =new ArrayList<>();
int count=0;
try {
Thread.sleep(50);
System.out.println("ccc");
count=1;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("bbb");
if(count ==1){
CcmsAttList entity=new CcmsAttList();
entity.setAttId(1+"");
entity.setAttRefId("A"+1);
syncServiceImpl.test3(entity.getAttId(),entity.getAttRefId());
}
System.out.println("2222222");
return "success";
}
}
- service
package com.chinacoal.microservice.impl.service; /** * @author sc * @createTime 2019/12/18 11:15 * @description */ public interface SyncAttfileService { /** * 異步 生成預覽地址 * @param attId 附件id * @param attRefId 附件地址 */ public void syncPreview(String attId, String attRefId); public String getTest1(); public String getTest2(); public void test3(String attId, String attRefId); }
serviceImpl
package com.chinacoal.microservice.impl.service.impl; import com.chinacoal.microservice.api.FileManagerClient; import com.chinacoal.microservice.impl.mapper.CcmsAttListMapper; import com.chinacoal.microservice.impl.service.SyncAttfileService; import com.chinacoal.microservice.model.attachment.CcmsAttList; import com.chinacoal.microservice.util.result.CodeMsg; import com.chinacoal.microservice.util.result.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author sc * @createTime 2019/12/18 10:39 * @description */ @Service @Slf4j public class SyncAttfileServiceImpl implements SyncAttfileService { @Resource private FileManagerClient fileManagerServiceClient; @Autowired private CcmsAttListMapper ccmsAttListMapper; /** * 順序調用 * @return */ public String getTest1(){ try { Thread.sleep(1000); System.out.println("順序調用"); } catch (InterruptedException e) { e.printStackTrace(); } return Thread.currentThread().getName()+"順序調用執行完畢"; } /** * 異步調用 有@Async註解的方法,默認就是異步執行的,會在默認的線程池中執行,但是此方法不能在本類調用;啓動類需添加直接開啓異步執行@EnableAsync * @return */ @Async public String getTest2(){ try { Thread.sleep(1000); System.out.println("異步調用"); } catch (InterruptedException e) { e.printStackTrace(); } return Thread.currentThread().getName()+"異步調用執行完畢"; } /** * 異步調用 有@Async註解的方法,默認就是異步執行的,會在默認的線程池中執行,但是此方法不能在本類調用;啓動類需添加直接開啓異步執行@EnableAsync * @return */ @Async public void test3(String attId, String attRefId){ log.info("異步生成文件預覽地址接受參數附件id :"+attId +" 附件引用地址:"+attRefId); try { Thread.sleep(1000); System.out.println("異步調用"+attId); } catch (Exception e) { e.printStackTrace(); log.info("調文件管理服務異常:原文件轉其他文件" +CodeMsg.ERROR_FILE_UPLOAD.fillArgs(e.getMessage())); } } /** * 異步 生成預覽地址 * @param attId * @param attRefId */ @Override @Async public void syncPreview(String attId, String attRefId){ log.info("異步生成文件預覽地址接受參數附件id :"+attId +" 附件引用地址:"+attRefId); //原文件 轉其他文件 Result<String> resultUpload = null; try { CcmsAttList ccmsAttList=new CcmsAttList(); resultUpload = fileManagerServiceClient.preview(attRefId); int code = resultUpload.getCode(); if(code != 10000){ log.info("調文件管理服務異常:原文件轉其他文件" +resultUpload.getMsg()); return ; } String previewPath= resultUpload.getData(); log.info("原文件轉其他文件返回值:"+previewPath); ccmsAttList.setAttId(attId); ccmsAttList.setPreviewPath(previewPath); ccmsAttListMapper.updateById(ccmsAttList); } catch (Exception e) { e.printStackTrace(); log.info("調文件管理服務異常:原文件轉其他文件" +CodeMsg.ERROR_FILE_UPLOAD.fillArgs(e.getMessage())); } } }
測試
調用test1 同步執行
測試test2 異步調用
測試test3 異步
參照文檔
https://blog.csdn.net/zhanaolu4821/article/details/80941825
https://blog.csdn.net/weixin_39800144/article/details/79046237