专栏首页java思维导图不仅会用@Async,我把源码也梳理了一遍(中)

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

好了,距离上次发表《 不仅会用@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思维导图,给我点个在看或者转发一下,万分感谢哈!

本文分享自微信公众号 - java思维导图(java-mindmap),作者:吕一明

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

    说起异步化,很多人会想起异步线程、消息队列等,消息队列不是文章的主题,今天我们来聊聊spring对异步化的支持@EnableAsync&@Async。

    java思维导图
  • 十分钟搞懂Java效率工具Lombok使用与原理

    Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它。Lombok是一种Java™实用工具,可...

    java思维导图
  • 图片验证码的需求分析、优雅实现

    通常我们最登录的时候,为了防止多次尝试或攻击登录接口,我们需要弄一个验证码的功能,只有输入验证码正确的情况下,我们才会去做密码校验,这样就减少了密码可能会被试出...

    java思维导图
  • 开源的.NET媒体文件操作组件TagLib#解析

      人生得意须尽欢 莫使金樽空对月。写文章都会在吃饭后,每次吃饭都要喝上二两小酒,写文章前都要闲扯,这些都是个人爱好,改不掉了,看不惯的人,还望多多包含一下,有...

    彭泽0902
  • 2019-03-26 Gitlab的第一个shared Runners配置(shared类型)

    在安装和配置完gitlab后,普通的代码管理功能都能正常使用了,现在配置一下gitlab runner用于代码的自动编译和部署。我下面的实例中定义的是share...

    Albert陈凯
  • 【死磕 Spring】----- IOC 之分析 BeanWrapper

    在实例化 bean 阶段,我们从 BeanDefinition 得到的并不是我们最终想要的 Bean 实例,而是 BeanWrapper 实例,如下:

    用户1655470
  • 线程的加入

    用户2965768
  • bootstrap 面板 带表格 常用样式

    <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel...

    用户5760343
  • 亲手养成一只自己的动漫主播!单张头像生成动画,可指定姿态或真人视频迁移

    有个小哥因为对动漫网红主播太着迷了,于是他用深度神经网络简化了动画生成过程。具体来说,就是将动漫人物的脸部和期望的姿势等图像输入神经网络,从而生成给定姿势的输出...

    大数据文摘
  • Ubuntu如何使用Roundcube安装自己的Webmail客户端

    如今,许多人使用基于浏览器的电子邮件客户端(如Gmail)来访问他们的电子邮件。但是,如果您想在查看电子邮件时停止查看广告,或者您已从公共电子邮件服务移至您自己...

    挺问中原

扫码关注云+社区

领取腾讯云代金券