Spring源码学习之:@async 方法上添加该注解实现异步调用的原理
在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率。今天我们来探讨下 spring 是如何完成这个功能的。
spring
在扫描bean的时候会扫描方法上是否包含@async的注解,如果包含的,spring会为这个bean动态的生成一个子类,我们称之为代理类(?),
代理类是继承我们所写的bean的,然后把代理类注入进来,那此时,在执行此方法的时候,会到代理类中,代理类判断了此方法需要异步执行,就不会调用父类
(我们原本写的bean)的对应方法。spring自己维护了一个队列,他会把需要执行的方法,放入队列中,等待线程池去读取这个队列,完成方法的执行,
从而完成了异步的功能。我们可以关注到再配置task的时候,是有参数让我们配置线程池的数量的。因为这种实现方法,所以在同一个类中的方法调用,添加@async注解是失效的!,原因是当你在同一个类中的时候,方法调用是在类体内执行的,spring无法截获这个方法调用。
那在深入一步,spring为我们提供了AOP,面向切面的功能。他的原理和异步注解的原理是类似的,spring在启动容器的时候,会扫描切面所定义的
类。在这些类被注入的时候,所注入的也是代理类,当你调用这些方法的时候,本质上是调用的代理类。通过代理类再去执行父类相对应的方法,那spring只
需要在调用之前和之后执行某段代码就完成了AOP的实现了!
那最后我们还有一个问题,spring是如何动态的生成某一个类的子类的?代理类?
阅读目录
简单介绍:
Spring为任务调度与异步方法执行提供了注解支持。通过在方法上设置@Async注解,可使得方法被异步调用。也就是说调用者会在调用时立即返回,而被调用方法的实际执行是交给Spring的TaskExecutor来完成。
开启@Async注解:
<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 注解 -->
<task:executor id="annotationExecutor" pool-size="20"/>
同时加入<context:component-scan />扫描注解。
栗子:
为了比较,先来一个同步调用:
@Component
public class TestAsyncBean {
public void sayHello4() throws InterruptedException {
Thread.sleep(2 * 1000);//网络连接中 。。。消息发送中。。。
System.out.println("我爱你啊!");
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Test
public void test_sayHello4() throws InterruptedException, ExecutionException {
System.out.println("你不爱我了么?");
testAsyncBean.sayHello4();
System.out.println("回的这么慢, 你肯定不爱我了, 我们还是分手吧。。。");
Thread.sleep(3 * 1000);// 不让主进程过早结束
}
}
输出结果:
你不爱我了么?
我爱你啊!
回的这么慢, 你肯定不爱我了, 我们还是分手吧。。。
同步调用会按代码顺序依次进行下去,如果哪里需要等待,那么就阻塞在那里,不再向下继续进行。
使用@Async的异步调用:
@Component
public class TestAsyncBean {
@Async
public void sayHello3() throws InterruptedException {
Thread.sleep(2 * 1000);//网络连接中 。。。消息发送中。。。
System.out.println("我爱你啊!");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Autowired
private TestAsyncBean testAsyncBean;
@Test
public void test_sayHello3() throws InterruptedException, ExecutionException {
System.out.println("你不爱我了么?");
testAsyncBean.sayHello3();
System.out.println("你竟无话可说, 我们分手吧。。。");
Thread.sleep(3 * 1000);// 不让主进程过早结束
}
}
输出结果:
你不爱我了么?
你竟无话可说, 我们分手吧。。。
我爱你啊!
异步调用,通过开启新的线程来执行调用的方法,不影响主线程。异步方法实际的执行交给了Spring的TaskExecutor来完成。
上面这种方式是没有返回值的,下面尝试有返回值的异步调用:
@Component
public class TestAsyncBean {
@Async
public String sayHello2() throws InterruptedException {
Thread.sleep(2 * 1000);//网络连接中 。。。消息发送中。。。
return "我爱你啊!";// 调用方调用后会立即返回,所以返回null
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Autowired
private TestAsyncBean testAsyncBean;
@Test
public void test_sayHello2() throws InterruptedException, ExecutionException {
System.out.println("你不爱我了么?");
System.out.println(testAsyncBean.sayHello2());
System.out.println("你说的啥? 我们还是分手吧。。。");
Thread.sleep(3 * 1000);// 不让主进程过早结束
}
}
输出结果:
你不爱我了么?
null
你说的啥? 我们还是分手吧。。。
通过直接获取返回值得方式是不行的,这里就需要用到异步回调,异步方法返回值必须为Future<>,就像Callable与Future。
下面通过AsyncResult<>来获得异步调用的返回值:
@Component
public class TestAsyncBean {
@Async
public Future<String> sayHello1() throws InterruptedException {
int thinking = 2;
Thread.sleep(thinking * 1000);//网络连接中 。。。消息发送中。。。
System.out.println("我爱你啊!");
return new AsyncResult<String>("发送消息用了"+thinking+"秒");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Autowired
private TestAsyncBean testAsyncBean;
@Test
public void test_sayHello1() throws InterruptedException, ExecutionException {
Future<String> future = null;
System.out.println("你不爱我了么?");
future = testAsyncBean.sayHello1();
System.out.println("你竟无话可说, 我们分手吧。。。");
Thread.sleep(3 * 1000);// 不让主进程过早结束
System.out.println(future.get());
}
}
输出结果:
你不爱我了么?
你竟无话可说, 我们分手吧。。。
我爱你啊!
发送消息用了2秒
Spring源码学习之:@async 方法上添加该注解实现异步调用的原理的更多相关文章
- Spring @async 方法上添加该注解实现异步调用的原理
Spring @async 方法上添加该注解实现异步调用的原理 学习了:https://www.cnblogs.com/shangxiaofei/p/6211367.html 使用异步方法进行方法调用 ...
- @async 方法上添加该注解实现异步调用的原理
在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率.今天我们来探讨下 spring 是如何完成这个功能的. spring 在扫描bean的 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
- spring源码学习之路---深入AOP(终)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- 【目录】Spring 源码学习
[目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ...
随机推荐
- CSSOM之getComputedStyle,currentStyle,getPropertyValue,getAttribute
js关于CSSOM编程的样式相关几个常用的方法 webkit:getComputedStyle,getPropertyValue IE:currentStyle,getAttribute 前言 jqu ...
- win安装NLTK出现的问题
一.今天学习Python自然语言处理(NLP processing) 需要安装自然语言工具包NLTK Natural Language Toolkit 按照教程在官网https://pypi.pyth ...
- redis 数据导出
一.导出所有的keys echo "keys 201*" |./redis-cli -h localhost -p 6379 -a password >> 1.txt ...
- (五)AOS编程
一.LOG AOS_LOG(index) //断言,会打印出断言传进来的值 AOS_ASSERT(0); //只会打印断言位置 return AOS_FAIL; //返回错误,函数 ...
- Android之源码之模块编译和调试
Android之源码之模块编译调试 (一) 进行源码模块修改进行编译的调试 1.首先是从git或者svn上拉一套完整的工程下来,然后全编一下,一般这个时间比较长,大概会得2,3个小时左右, 2,编译成 ...
- Unity中有两种Animation Clip
http://blog.csdn.net/zzxiang1985/article/details/51291861 在Unity中,我们有两种方法创建Animation Clip. 一种(后面简称方法 ...
- Python学习推荐
1. Python官网 官网想必是最权威的,不仅有Python 2.X和3.X的软件包,还有官方文档Python tutorial (official docs)及社区. 2. 在线阅读免 ...
- 图的深度优先和广度优先遍历(图以邻接表表示,由C++面向对象实现)
学习了图的深度优先和广度优先遍历,发现不管是教材还是网上,大都为C语言函数式实现,为了加深理解,我以C++面向对象的方式把图的深度优先和广度优先遍历重写了一遍. 废话不多说,直接上代码: #inclu ...
- java写hadoop全局排序
前言: 一直不会用java,都是streaming的方式用C或者python写mapper或者reducer的可执行程序.但是有些情况,如全排序等等用streaming的方式往往不好处理,于是乎用原生 ...
- 困扰我多年的Connection reset问题
第一次出现:是thrift的python client去请求server,发现偶尔出现这个问题 第二次:接入第三方的api,去请求数据时,发现一个接入方的api第一次总是报这个错,当时又没有做处理,导 ...