前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >不仅会用@Async,我把源码也梳理了一遍(中)

不仅会用@Async,我把源码也梳理了一遍(中)

作者头像
java思维导图
发布2019-09-25 11:23:38
1.2K0
发布2019-09-25 11:23:38
举报

好了,距离上次发表《 不仅会用@Async,我把源码也梳理了一遍(上)》已经好几天了,上一篇文章中我们介绍了@EnableAsync+@Async的简单用法,今天我们来分析一下它的底层原理。

不过在分析源码之前,我们先来做下猜想,也就是利用我们现有知识去猜想一下它的原理。

猜想

  • @EnableAsync
  • @Async

@EnableAsync是开启异步的功能,应该需要完成某个配置的初始化,初始化过程至少应该包括扫描所有的@Async的注解,既然是异步执行,我们看了输入日志,线程名称为task,所以可以理解为一个任务或者一个线程,创建线程的方式常用的有如下3种:

  • 继承Thread类创建
  • 通过Runnable接口创建线程类
  • 使用Callable和Future创建线程

基于使用@Aysnc的方法可以返回值为Future,所以应该倾向于使用Callable创建线程的方式。

我们再回到扫描@Async的过程,@Async是个注解,一般要处理一个注解标识的方法,我们可以联想到spring aop,在注解方法前后添加一些业务。

所以总结一下我们的联想:@EnableAsync注解加载模块过程中,定义了一个切面,使用@Async作为切点,然后执行环绕通知过程中,新建了一个Callable线程,把我们的原本方法的执行放到了线程内部执行。

我的推理强不强?请叫我工藤新一!

预备知识

其实呀,上面我所说的已经很接近真相了,不过在说源码之前,我还是要给大家先来一次预备知识的预习。

Callable创建线程

说不定很多人都不懂这个,先讲讲吧~

class CallableDemo implements Callable<String>{
 
    @Override
    public String call() throws Exception {
        //线程业务逻辑
        return "关注公众号:java思维导图";
    }
}

执行Callable,需要FutureTask 的支持,可以接受线程结果。所以执行线程是这样的:

CallableDemo demo = new CallableDemo();
FutureTask<String> future = new FutureTask<>(demo);
new Thread(future).start();

// 获取线程结果
String result = future.get();

上篇文章中,我们是不是也是使用了future.get()来获取结果呀?哈哈~

切面编程aop

说起spring aop,我们一般都是通过注解式定义一个aop,常用的的几个注解如下:

  • @Aspect
    • 标注增强处理类(切面类)
  • @Pointcut
    • 自定义切点位置
  • @Around
    • 定义增强,环绕通知处理

这aop注解我就不举例子了,其实你知道用编码实现怎么去定义一个切面么,因为注解式只是编码式实现的简便方式而已。我们来看一下,使用编码式如何去实现一个切面:

  • 定义一个需要被切面处理方法 com.example.demo.TestMethod
public class TestMethod {

    public void test() {
        System.out.println("关注公众号:java思维导图!");
    }
}
  • 通知方法 com.example.demo.TestMethodInterceptor,相当于@Around

虽然长得像拦截器,确实一个继承了*extends *Advice的拦截器,所以这个拦截器其实也是个通知。

@Component
public class TestMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before...");
        Object resObj = invocation.proceed();
        System.out.println("after...");
        return resObj;
    }
}
  • 定义切面编程
@Autowired
TestMethodInterceptor interceptor;

@Test
public void testAop() {
    TestMethod delegate = new TestMethod();

    // 准备通知
    TestMethodInterceptor interceptor = new TestMethodInterceptor();

    ProxyFactory factory = new ProxyFactory();

    // 准备切点
    JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
    pointcut.setPattern("com.example.demo.TestMethod.*");

    // 切面 = 切点 + 通知
    Advisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);

    // 给代理工厂一个切面
    factory.addAdvisor(advisor);

    // 需要被代理的对象
    factory.setTarget(delegate);

    TestMethod proxy = (TestMethod) factory.getProxy();

    proxy.test();
}

从上面代码可以看到,我们需要执行TestMethod 的test方法,但是我们以这个方法为切点做了个切面代理,切面的通知处理方法中我们加入了自己的一些业务逻辑,是不是和我们常用的aop功能一致了。

怕有些人还是懵逼,再梳理一下,上面的代码中,我使用ProxyFactory定义了一个代理,需要代理的切点是com.example.demo.TestMethod.*,然后还要定义一个切面环绕处理方法,就是这个拦截器又是通知处理器的TestMethodInterceptor,处理的方法就是我们的invoke方法。所以你会发现,通常我们说拦截器拦截的都是url,这里我们拦截的是一个类的所有方法。这个其实就是aop的原理。

测试结果:

好了,讲了这么多预备知识,有了预备知识之后我们再去看源码会清晰很多,因为其实你都可以使用这两个预备知识自己去实现一个@Async了。

总结

一步一步来,从猜想到预备知识,再到源码分析,逐步验证我们的结果,既能学到源码,又能从源码中加深已有知识的综合运用。强大的框架不都是这样写出来的吗?

好啦,今天的文章先到这里了。

如果你喜欢我的文章,欢迎关注我的公众号:java思维导图,给我点个在看或者转发一下,万分感谢哈!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java思维导图 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Callable创建线程
  • 切面编程aop
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档