2、使用方式(基于下)
需要在启动类加入@使异步调用@Async注解生效
在需要异步执行的方法上加入此注解即可@Async(""),为自定义线程池。
代码略。。。就俩标签,自己试一把就可以了
3、注意事项
在默认情况下,未设置时,默认是使用tor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。所以最好我们来自定义一个线程池。
调用的异步方法,不能为同一个类的方法(包括同一个类的内部类),简单来说,因为在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。
其他的注解如@Cache等也是一样的道理,说白了,就是的代理机制造成的。所以在开发中异步调用,最好把异步服务单独抽出一个类来管理。下面会重点讲述。。
4、什么情况下会导致@Async异步方法会失效?
调用同一个类下注有@Async异步方法:
在中像@Async和@、cache等注解本质使用的是动态代理,其实容器在初始化的时候容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过容器,那么解决方法也会沿着这个思路来解决。
调用的是静态( )方法
调用()私有化方法
5、解决4中问题1的方式(其它2,3两个问题自己注意下就可以了)
将要异步执行的方法单独抽取成一个类,原理就是当你把执行异步的方法单独抽取成一个类的时候,这个类肯定是被管理的,其他组件需要调用的时候肯定会注入进去,这时候实际上注入进去的就是代理类了。
其实我们的注入对象都是从容器中给当前组件进行成员变量的赋值,由于某些类使用了AOP注解,那么实际上在容器中实际存在的是它的代理对象。那么我们就可以通过上下文获取自己的代理对象调用异步方法。
@Controller
@RequestMapping("/app")
public class EmailController {
//获取ApplicationContext对象方式有多种,这种最简单,其它的大家自行了解一下
@Autowired
private ApplicationContext applicationContext;
@RequestMapping(value = "/email/asyncCall", method = GET)
@ResponseBody
public Map asyncCall () {
Map resMap = new HashMap();
try{
//这样调用同类下的异步方法是不起作用的
//this.testAsyncTask();
//通过上下文获取自己的代理对象调用异步方法
EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);
emailController.testAsyncTask();
resMap.put("code",200);
}catch (Exception e) {
resMap.put("code",400);
logger.error("error!",e);
}
return resMap;
}
//注意一定是public,且是非static方法
@Async
public void testAsyncTask() throws InterruptedException {
Thread.sleep(10000);
System.out.println("异步任务执行完成!");
}
}
开启cglib代理,手动获取代理类,从而调用同类下的异步方法。首先,在启动类上加上@xy( = true)注解。代码实现,如下:
@Service
@Transactional(value = "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!!!");
}
}
三、异步请求与异步调用的区别
两者的使用场景不同,异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量;而异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析等。
异步请求是会一直等待相应的,需要返回结果给客户端的;而异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。
四、总结
异步请求和异步调用的使用到这里基本就差不多了,有问题还希望大家多多指出。这边文章提到了动态代理异步调用,而中Aop的实现原理就是动态代理,后续会对动态代理做详细解读,还望多多支持哈。
限时特惠:本站持续每日更新海量各大内部创业课程,一年会员仅需要98元,全站资源免费下载
点击查看详情
站长微信:Jiucxh