二、Spring Boot中异步调用的使用
1、介绍
异步请求的处理 。除了异步请求,一般上我们用的比较多的应该是异步调用 。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的 。比如记录日志信息等业务 。这个时候正常就是启一个新线程去做一些业务处理,让主线程异步的执行其他业务 。2、使用方式(基于spring下)
需要在启动类加入@EnableAsync使异步调用@Async注解生效
在需要异步执行的方法上加入此注解即可@Async("threadPool"),threadPool为自定义线程池 。
代码略 。。。就俩标签,自己试一把就可以了
3、注意事项
在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程 。可通过控制台日志输出可以看出,每次输出线程名都是递增的 。所以最好我们来自定义一个线程池 。
【学会使用 Spring Boot 的异步调用】调用的异步方法,不能为同一个类的方法(包括同一个类的内部类),简单来说,因为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容器中实际存在的是它的代理对象 。那么我们就可以通过上下文获取自己的代理对象调用异步方法 。
@Controller@RequestMapping("/app")public class EmailController { //获取ApplicationContext对象方式有多种,这种最简单,其它的大家自行了解一下 @Autowired private ApplicationContext applicationContext; @RequestMapping(value =https://www.isolves.com/it/cxkf/kj/2020-12-11/ "/email/asyncCall", method = GET) @ResponseBody public Map
开启cglib代理,手动获取Spring代理类,从而调用同类下的异步方法 。首先,在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)注解 。代码实现,如下:
@Service@Transactional(value =https://www.isolves.com/it/cxkf/kj/2020-12-11/ "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)public class EmailService { @Autowired private ApplicationContext applicationContext; @Async public void testSyncTask() throws InterruptedException { Thread.sleep(10000); System.out.println("异步任务执行完成!"); } public void asyncCallTwo() throws InterruptedException { //this.testSyncTask();// EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);// emailService.testSyncTask(); boolean isAop = AopUtils.isAopProxy(EmailController.class);//是否是代理对象; boolean isCglib = AopUtils.isCglibProxy(EmailController.class); //是否是CGLIB方式的代理对象; boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class); //是否是JDK动态代理方式的代理对象; //以下才是重点!!! EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class); EmailService proxy = (EmailService) AopContext.currentProxy(); System.out.println(emailService == proxy ? true : false); proxy.testSyncTask(); System.out.println("end!!!"); }}
推荐阅读
- 使用Python+Fabric实现Linux自动化操作
- 硬盘变RAW了怎么办?
- 凭借这5步,我30分钟学会了Python爬虫
- WSL2 中使用 systemctl 命令
- 交管12123使用时间段是多久?
- 逾期很久的信用卡还清了还能继续用吗? 逾期过后的信用卡还清后还可以使用么
- 使用 UEFI 双启动 Windows 和 Linux
- 很多人学Spring框架,总觉得IOC模糊不清?
- 使用python爬取抖音app视频
- 使用Docker镜像