范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Spring异步实现原理与实战分享

  前言:
  最近因为全链路压测项目需要对用户自定义线程池 Bean 进行适配工作,我们知道全链路压测的核心思想是对流量压测进行标记,因此我们需要给压测的流量请求进行打标,并在链路中进行传递,那么问题来了,如果项目中使用了多线程处理业务,就会造成父子线程间无法传递压测打标数据,不过可以利用阿里开源的 ttl 解决这个问题。
  全链路压测项目的宗旨就是不让用户感知这个项目的存在,因此我们不可能让用户去对其线程池进行改造的,我们需要主动去适配用户自定义的线程池。
  在适配过程的过程中无非就是将线程池替换成 ttl 去解决,可通过代理或者替换 Bean 的方式实现,这方面不是本文的内容,本文主要是深入 Spring 异步实现的原理,让大家对 Spring 异步编程不再陌生!
  正文: 运行原理分析
  过一遍源码分析,才能知道其中的一些细节原理,这也是不可避免的过程,虽然我也不想在文章中贴过多的源码,但如果不从源码中得出原因,很可能你会知其然不知其所以然。下面就尽量跟着源码走一遍它的运行机制是怎么样的,我把我自己的理解也会尽量详细地描述出来,在这里我会将其关联的源码贴出来分析,这些源码都有其相互关联性,可能你看到后面还会回来再看一遍。 注册通知器过程
  开启 Spring 异步编程之需要一个注解即可: @EnableAsync
  Springboot 中有非常多 @Enable* 的注解,其目的是显式开启某一个功能特性,这也是一个非常典型的编程模型。
  @EnableAsync 注解注入了一个 AsyncConfigurationSelector 类,这个类目的就是为了注入 ProxyAsyncConfiguration 自动配置类,它的父类 AbstractAsyncConfiguration 做了件事情:
  org.springframework.scheduling.annotation.AbstractAsyncConfiguration#setConfigurers
  我们可以实现 AsyncConfigurer 接口的方式去自定义一个线程池 Bean,这个后面会会讲到,源码所示,这里目的是为了这个 bean,并将其定义的线程池对象和异常处理对象保存到 AsyncConfiguration 中,用于创建 AsyncAnnotationBeanPostProcessor 。
  这两个对象后面源码分析会再次遇上。
  而这个配置类就是为了注册一个名为 AsyncAnnotationBeanPostProcessor 的 bean,如其名,它是一个 BeanPostProcessor 处理器,它的类继承结构如下所示:
  从类继承结构可以看出,AsyncAnnotationBeanPostProcessor 实现了 BeanPostProcessor 和 BeanFactoryAware,因此 AsyncAnnotationBeanPostProcessor 会在 setBeanFactory 方法中做了 Spring 异步编程中最为重要的一步,创建一个针对 @Async 注解的通知器 AsyncAnnotationAdvisor(叫做切面貌似也可以),这个通知器主要用于拦截被 @Async 注解的方法。同时,bean 实例初始化过程会被 AsyncAnnotationBeanPostProcessor 拦截处理,处理过程会将符合条件的 bean 注册 AsyncAnnotationAdvisor :
  org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
  创建通知器过程
  接下来我们就分析 AsyncAnnotationAdvisor 是如何创建的。
  AsyncAnnotationAdvisor 实现了 PointcutAdvisor 接口,因此需要同时实现 getPointcut 和 getAdvice 方法,而这两个方法的实际内容有以上红框创建实现。
  到这里我们已经知道,Spring 的异步实现原理,是利用 Spring AOP 切面编程实现的,通过 BeanPostProcessor 拦截处理符合条件的 bean,并将切面织入,实现切面增强处理。
  Spring AOP 编程核心概念: Advice:通知,切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间;
  Pointcut:切点,切入点指切面具体织入的方法;
  Advisor:切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。
  因此我们需要创建一个切面和切入点: buildAdvice:
  buildAdvice 方法可知,切面是一个 AnnotationAsyncExecutionInterceptor 类,该类实现了 MethodInterceptor 接口,其 invoke 方法即为拦截处理的核心源码,后面会进行详细分析。 buildPointcut:
  从 AsyncAnnotationAdvisor 构造器中可以看出,buildPointcut 方法目的就是为了创建 @Async 注解的切入点。 通知器拦截处理过程
  前面我们已经知道,拦截切面是一个 AnnotationAsyncExecutionInterceptor 类,我们直接定位到 invoke 方法一探究竟:
  org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke
  拦截处理的核心逻辑就是这么简单,也没啥好分析的,无非就是匹配方法指定的线程池,接着构建执行单元 Callable,最后调用 doSubmit 方法执行。 如何匹配线程池?
  重点在于如何匹配线程池,这也是后面实战分析的重点内容,因此我们需要在这里详细分析匹配线程池的一些策略细节。
  org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
  getExecutorQualifier 方法目的是获取 @Async 注解上的 value 值,value 值即线程池 Bean 的名称,如果获取到的 targetExecutor 不是 Spring 类型的线程池,则使用 TaskExecutorAdapter 进行适配,这也是为什么我们直接创建 Executor 类型的线程池 Spring 也是支持的原因。
  从以上源码逻辑可看出如果我们使用 @Async 注解时 value 值为空,Spring 就会使用 defaultExecutor ,defaultExecutor 是什么时候赋值的呢?上面内容已经有提及,在 buildAdvice 方法创建 AnnotationAsyncExecutionInterceptor 时 调用了其 configure 方法,如下:
  org.springframework.aop.interceptor.AsyncExecutionAspectSupport#configure
  原来当 defaultExecutor 和 exceptionHandler 是当初从 ProxyAsyncConfiguration 中获取用户自定义的 AsyncConfigurer 实现类而来的,那么如果 defaultExecutor 不存在怎么办?从源码可看出,defaultExecutor 其实是一个 SingletonSupplier 类型,如果调用 get 方法不存在,则使用默认值,默认值为: () -> getDefaultExecutor(this.beanFactory);
  org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor
  注意第一个红框的注释,此时 Spring 寻找默认的线程池 Bean 为指定 Spring 的 TaskExecutor 类型,并非 Executor 类型,如果 Bean 容器中没有找到 TaskExecutor 类型的 Bean,则继续寻找默认为以下名称的 Bean: public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
  那么如果都没有找到怎么办呢?在这个方法直接返回 null 了,AsyncExecutionInterceptor 类覆写了 这个方法:
  org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor
  如果没有找到,则直接创建一个 SimpleAsyncTaskExecutor 类作为 @Async 注解底层使用的线程池。
  从匹配线程池源码得知,如果你创建的线程池 Bean 非TaskExecutor 类型并且没有使用实现 AsyncConfigurer 接口方式创建线程池,就需要主动指定线程池 Bean 名称,否则 Spring 会使用默认策略。 总结
  利用 BeanPostProcessor 机制在 Bean 初始化过程中创建一个 AsyncAnnotationAdvisor 切面,并且符合条件的 Bean 生成代理对象并将 AsyncAnnotationAdvisor 切面添加到代理中。
  可以看出 Spring 的很多功能都是围绕着 Spring IOC 和 AOP 实现的。 Spring 默认线程池策略分析
  有时候为了方便,我们不自定义创建线程池 bean 时,Spring 默认会为我们提供什么样的线程池呢?
  我们先来看下结果:
  很奇怪,明明我们都没有在项目中自定义线程池 Bean,按照以上源码的分析结果来看,此时 Spring 选择的是 SimpleAsyncTaskExecutor 才对,莫非是 super#getDefaultExecutor 方法找到了线程池 Bean?
  从以上截图确实是找到了,而且类型还是 ThreadPoolTaskExecutor 类型的,那可以推断出 Spring 一定是在某个地方创建了一个 ThreadPoolTaskExecutor 类型的 Bean。
  果然,在 spring-boot-autoconfigure 2.1.3.RELEASE 中,会在 org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration 中自动创建一个默认的 ThreadPoolTaskExecutor bean,getDefaultExecutor 方法会在容器中找到这个bean,并将其作为默认的 @Async 注解的执行线程池。
  这里我为什么要标注版本呢?因为某些低版本的 spring-boot-autoconfigure,是没有 TaskExecutionAutoConfiguration 的,此时 Spring 就会选择 SimpleAsyncTaskExecutor。
  org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
  从以上源码可以看出,默认的线程池的参数还可以手动在 properties 中配置,这意味着不需要主动创建线程池的情况下,也可以通过 properties 配置文件更改线程池相关参数。 创建线程池 Bean 的几种方式
  1、直接创建一个 Bean 的方式,这貌似是最多人使用的方式,可以创建多个线程池 Bean,使用时指定线程池 Bean 名称: @Bean("myTaskExecutor_1") public Executor getThreadPoolTaskExecutor1() {   final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();   // set ...   return executor; }  @Bean("myTaskExecutor_2") public Executor getThreadPoolTaskExecutor2() {   final ThreadPoolExecutor executor = new ThreadPoolExecutor();   // set ...   return executor; }
  2、实现 AsyncConfigurer 接口方式: @Component public class AsyncConfigurerTest implements AsyncConfigurer {    private static final Logger LOGGER = LoggerFactory.getLogger(AsyncConfigurerTest.class);    @Override   public Executor getAsyncExecutor() {     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();     // set ...     return executor;   }    @Override   public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {     return (ex, method, params) -> {       LOGGER.info("Exception message:{}", ex.getMessage(), ex);       LOGGER.info("Method name:{}", method.getName());       for (Object param : params) {         LOGGER.info("Parameter value:{}", param);       }     };   } }
  这种方式可以方便定义异常处理的逻辑,不过从源码分析可以看出,项目中只能存在一个 AsyncConfigurer 的配置,意味着我们只能通过 AsyncConfigurer 配置一个自定义的线程池 Bean。
  3、利用 spring-boot-autoconfigure 在 properties 配置线程池参数:
  前面讲到了 Spring 默认线程池策略,这里利用 spring-boot-autoconfigure 默认创建一个 ThreadPoolTaskExecutor,通过 properties 自定义线程池相关参数。
  这个方式的缺点就是类型固定为 ThreadPoolTaskExecutor,且只能有一个线程池。 总结了很多有关于java面试的资料,希望能够帮助正在学习java的小伙伴。由于资料过多不便发表文章,创作不易,望小伙伴们能够给我一些动力继续创建更好的java类学习资料文章,
  请多多支持和关注小作,别忘了点赞+评论+转发。右上角私信我回复【03】即可领取免费学习资料谢谢啦!
  作者:  张乘辉
  原文出处:https://mp.weixin.qq.com/s/mNDwZfPK90RoGmJGHCjKrw

为什么越来越多的人,不喜欢逛专柜买东西?哈喽,大家好,我是甘老师。最近几年,我发现很少有人去专柜买护肤品,一方面,直播带货的兴起,让更多人选择网上购物另一方面,国内专柜导购时常跟在你身后,对社恐朋友来讲,真的是大型尴尬现围观!2022年最佳5款摄影手机小白也能拍大片,喜欢哪款?哈喽,您好!我是原呵呵,点点关注吧,更多精彩内容等着您智能手机的价格比几年前要贵得多。许多智能手机试图提供最好的规格以削弱其他玩家。然而,有一个决定性因素总是决定智能手机的成败,那年轻人消费看重性价比,黄金市场一夜大火,唯品会数据揭开实情最近,网上一个叫10个年轻人有7个花钱先看性价比的话题火了,让大量95后00后产生了强烈共鸣,纷纷参与讨论。话题更是一度霸榜热搜,单日阅读量达1亿。确实,现在年轻人的消费观就是这样黄金首饰如何回收变现?很多人都喜欢金首饰,主要原因是因为黄金保值,而且容易变现。那么黄金怎么回购?如何实现变现呢?一黄金首饰回购与变现渠道1。黄金回收变现可以通过原品牌店进行回收,一般情况下可以提供旧物被名字耽误的尖货,唐族TANGZUSHIMINLI李世民单动圈耳塞去年9月因为大佬的关系邂逅了一个耳机新牌唐朝TFORCE,首个产品居然拉了唐高祖李渊来站队,还号称是唐朝三部曲的第一部。没想到,上海刚复工复产,就收到了三部曲的第二部,依然是单动圈这么近那么美周末到河北(9)岸下石窑小镇,沉浸在山间的乡野气息微游河北这么近那么美周末游河北河北文旅看图识景我爱石家庄蓝天白云奋进新时代美丽石家庄提起小镇乡间村落,每一个地域的人,都会在脑海中产生不同的景象江浙地区,是江南的烟雨小城,是铺满青红米K50Ultra三大升级,一个遗憾,基本稳了?我们都知道,为了可以没负担地冲击高端,雷军把红米品牌交给卢伟冰独立运营,目的是继承性价比的定位,保住中低端的基本盘。不过,从各种迹象来看,卢伟冰的目标并不局限于中低端,而是要拿下3离开央视直播惨淡,隐婚老公首曝光,主持人经纬走错了哪一步?近日,离开央视7年的美女主持人经纬正在社交平台上直播时,她的老公罕见地第一次出现。在此之前,经纬只晒出过自己5岁的儿子,发布的视频中,经纬和儿子常常一起做些有趣的事情高考期间画画为辛巴坚定优化辛选供应链,四川90后大学生海虾淡养敲开村民致富门随着直播电商的火爆,紧随其后的是直播形式内容与话术的同质化现象,消费者对此的新鲜感逐渐减弱,行业内的竞争日趋激烈。面对这一形势,作为头部直播电商企业的创始人,辛巴带领辛选集团做出了一张明星卡价格炒至18000元!说唱巅峰,还是小卡巅峰?每经记者谢陶每经实习记者石普宁每经编辑唐元我以为这样的行情差不多了,昨晚战斗到凌晨2点才睡,今天起来就起飞了。7月23日,潮玩玩家小土,在社交平台分享自己中国说唱巅峰对决明星卡的囤保险缴纳5年后退保能退多少?怎么退损失比较少?当初购买保险的时候主要是为了能够给我们的生活添加一份保障,于是很多消费者除了给自己购买报下你以外,还会为自己的家人购买一份。但是有大部分投保人是第一次购买,然后买到不适合自己的,那
河南地区广袤无垠的土地适合徒步远行河南的地势以平原为主,土地广袤,是中华民族的发源地。这里也是中华民族的血脉和精神的纽带,千百年来脸朝黄土背朝天,孜孜不倦。这里的每一位百姓脚下踏过的每一寸黄土都是沉重的枷锁。他们想怎样减肥降脂肪?研究发现使用加重背心效果好根据我们社区统计,90左右2型糖尿病人有体重或者腰围超标,这些脂肪增加会加重糖尿病的管理难度,因此需要减少多余脂肪。但是,运动低能量减肥往往比较难坚持,见效慢。怎么来提出减肥效果呢两个利空消息!A股,下周行情预判炒股,每个人都会遇到泥沙俱下的跌幅,如果每个人都亏损了,就相当于没有亏损。这句话很难理解,只能自己去领悟,包括后面一句人人盈利,就是没有人盈利。真正的亏损,是自己追涨杀跌,或者遇坑人生中时间和金钱是治愈一切的良药人生中什么是治愈一切的良药?人生中什么是治愈一切的良药?我认为有两样东西时间和金钱。虽然,不敢说是万能,百分之百管用,但是,绝对效果非常好。属先说时间,它是治愈感情创伤的最好良药。世界杯观看指南2022卡塔尔世界杯的赛程表具体是怎样安排的?2022年卡塔尔世界杯将在2022年1112月举行,整届世界杯将进行64场比赛,其中小组赛共48场,比赛时间是11月21日12月2日,具体比赛时间详见文章后面的图片。18决赛的比赛拼多多海外版上市两个月,冲上美国榜首!80小伙把酒馆开进海外砍一刀的拼多多相信大家都很熟悉,然而近段时间其可谓是好消息不断,多次荣登热搜榜前列。拼多多海外版上市两个月,冲上美国榜首!比如前段时间,其公布的第二季度财报中,其营收依旧实现了高达世界上最快的步行鞋初创公司Shift机器人公司推出了月光鞋模型,通过AI算法帮助佩戴者提高250的步行速度。视频加载中由一群机器人工程师开发,他们在美国卡内基梅隆大学工作,在创立名为Shift机器人恐慌式下跌,大A下周形势怎么看本周道琼斯工业平均指数,近期连续上涨6个交易日,创10月份新高,10月份以来道琼斯指数涨幅超14,纳斯达克指数10月份探底回升,涨幅4。98,从这里我们可以看到,离11月份的加息时爆单!加急空运!卖出超30亿元,这样东西在国外火了由于能源价格飞涨,即将来临的冬天让欧洲人非常头疼。受此影响,我国保暖用品到欧洲市场的出口量明显增长,号称取暖小三件的帽子围巾手套,受到欧洲客户的欢迎。义乌国际商贸城的经营户章芳洁从绝味食品煌上煌业绩承压,卤味巨头将如何破局?大河报大河财立方(记者吴春波)第三季度财报出炉,鸭货卤味巨头并未等来市场销售的反转。10月26日晚间10月27日晚间,A股鸭货卤味代表企业绝味食品和煌上煌相继披露了三季度报,两家企明年起,做好资产贬值的准备?这5件事尽量不要接触对于绝大部分普通人来说,可能都会觉得近些年来的生活越来越不好过,其中作为明显的一个体现就是资产的缩水与贬值。不可否认的是,金钱是维持日常生活的基础,而对于普通人来说,自己的经济实力