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