专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

详细分析Spring的AOP源码

  前言
  本篇文章是SpringAOP的源码学习分享,分为上下两篇,在详细分析Spring的AOP源码上篇中已知SpringAOP的切面类织入业务bean后,会为业务bean生成动态代理对象,这个动态代理对象中持有需要生效的所有通知,叫做通知链。
  本篇将对调用AOP动态代理对象时的整个流程进行学习,以探究切面中的类似于前置通知或者后置通知这种通知方法是如何对目标bean的目标方法进行增强的。
  注:本文均基于JDK动态代理。正文一。AOP动态代理对象结构分析
  在上篇的示例工程中,可以看一下测试程序中从容器获取到的IMyService的bean是什么样子,调试图如下所示。
  可以看到获取出来的bean实际为MyService的JDK动态代理对象,InvocationHandler为JdkDynamicAopProxy,JdkDynamicAopProxy中持有ProxyFactory,ProxyFactory中持有目标对象和通知链。二。AOP动态代理对象调用分析
  调用动态代理对象的方法时,会调用到InvocationHandler的invoke()方法,这里InvocationHandler为JdkDynamicAopProxy,所以将JdkDynamicAopProxy的invoke()方法作为入口开始分析。publicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{ObjectoldProxynull;booleansetProxyContextfalse;TargetSourcetargetSourcethis。advised。targetSource;Objecttargetnull;try{if(!this。equalsDefinedAopUtils。isEqualsMethod(method)){不会将通知作用在equals()方法,除非目标对象实现的接口中定义了equals()方法returnequals(args〔0〕);}elseif(!this。hashCodeDefinedAopUtils。isHashCodeMethod(method)){不会将通知作用在hashCode()方法,除非目标对象实现的接口中定义了hashCode()方法returnhashCode();}elseif(method。getDeclaringClass()DecoratingProxy。class){returnAopProxyUtils。ultimateTargetClass(this。advised);}elseif(!this。advised。opaquemethod。getDeclaringClass()。isInterface()method。getDeclaringClass()。isAssignableFrom(Advised。class)){不会将通知作用于Advised接口或者其父接口中定义的方法returnAopUtils。invokeJoinpointUsingReflection(this。advised,method,args);}ObjectretVal;if(this。advised。exposeProxy){oldProxyAopContext。setCurrentProxy(proxy);setProxyContexttrue;}获取目标对象targettargetSource。getTarget();获取目标对象的Class对象Classlt;?targetClass(target!null?target。getClass():null);将通知链中能够作用于当前方法的通知组装成拦截器链ListObjectchainthis。advised。getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);if(chain。isEmpty()){Object〔〕argsToUseAopProxyUtils。adaptArgumentsIfNecessary(method,args);如果拦截器链是空,那么直接调用目标对象方法AopUtils。invokeJoinpointUsingReflection()最终会调用到method。invoke(target,args)retValAopUtils。invokeJoinpointUsingReflection(target,method,argsToUse);}else{创建方法调用器MethodInvocation,实际为ReflectiveMethodInvocation创建ReflectiveMethodInvocation时传入的参数依次为:代理对象,目标对象,目标方法,目标方法参数,目标对象的Class对象,拦截器链MethodInvocationinvocationnewReflectiveMethodInvocation(proxy,target,method,args,targetClass,chain);调用方法调用器的proceed()方法,开始进入调用各个通知方法和目标方法的递归流程retValinvocation。proceed();}Classlt;?returnTypemethod。getReturnType();if(retVal!nullretValtargetreturnType!Object。classreturnType。isInstance(proxy)!RawTargetAccess。class。isAssignableFrom(method。getDeclaringClass())){retValproxy;}elseif(retValnullreturnType!Void。TYPEreturnType。isPrimitive()){thrownewAopInvocationException(Nullreturnvaluefromadvicedoesnotmatchprimitivereturntypefor:method);}returnretVal;}finally{if(target!null!targetSource。isStatic()){targetSource。releaseTarget(target);}if(setProxyContext){AopContext。setCurrentProxy(oldProxy);}}}复制代码
  上述的invoke()方法主要是做了两件事情:第一件事情是将通知链中所有能够作用于当前目标方法的通知构建成拦截器链,并基于拦截器链生成方法调用器ReflectiveMethodInvocation;第二件事情就是调用ReflectiveMethodInvocation的proceed()方法,这个方法会调用到目标方法,并且在调用的过程中,拦截器链中的拦截器也会执行以达到增强功能的效果。
  下面先看一下通知链如何构建成拦截器链,this。advised实际就是生成动态代理对象的时候的ProxyFactory,而ProxyFactory继承于AdvisedSupport,将通知链构建成拦截器链的方法就是AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice(),如下所示。publicListObjectgetInterceptorsAndDynamicInterceptionAdvice(Methodmethod,NullableClasslt;?targetClass){MethodCacheKeycacheKeynewMethodCacheKey(method);先根据Method从缓存中拿拦截器链,缓存中没有时会去生成并缓存起来ListObjectcachedthis。methodCache。get(cacheKey);if(cachednull){实际调用到DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice()方法来生成拦截器链cachedthis。advisorChainFactory。getInterceptorsAndDynamicInterceptionAdvice(this,method,targetClass);this。methodCache。put(cacheKey,cached);}returncached;}复制代码
  每个目标方法对应的拦截器链在生成后都会被缓存,所以会先从缓存中拿拦截器链,缓存中没有时会去调用DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice()方法来生成拦截器链,getInterceptorsAndDynamicInterceptionAdvice()方法实现如下。publicListObjectgetInterceptorsAndDynamicInterceptionAdvice(Advisedconfig,Methodmethod,NullableClasslt;?targetClass){AdvisorAdapterRegistryregistryGlobalAdvisorAdapterRegistry。getInstance();先从ProxyFactory中将通知链获取出来Advisor〔〕advisorsconfig。getAdvisors();ListObjectinterceptorListnewArrayList(advisors。length);Classlt;?actualClass(targetClass!null?targetClass:method。getDeclaringClass());BooleanhasIntroductionsnull;for(Advisoradvisor:advisors){因为Advisor接口有两个子类接口,分别是PointcutAdvisor和IntroductionAdvisorif(advisorinstanceofPointcutAdvisor){处理PointcutAdvisor,示例中使用的都是PointcutAdvisorPointcutAdvisorpointcutAdvisor(PointcutAdvisor)advisor;if(config。isPreFiltered()pointcutAdvisor。getPointcut()。getClassFilter()。matches(actualClass)){获取切点对象,这里的mm类型为AspectJExpressionPointcutMethodMatchermmpointcutAdvisor。getPointcut()。getMethodMatcher();booleanmatch;if(mminstanceofIntroductionAwareMethodMatcher){if(hasIntroductionsnull){hasIntroductionshasMatchingIntroductions(advisors,actualClass);}判断当前Advisor是否能够作用于目标方法match((IntroductionAwareMethodMatcher)mm)。matches(method,actualClass,hasIntroductions);}else{判断当前Advisor是否能够作用于目标方法matchmm。matches(method,actualClass);}if(match){如果当前Advisor能够作用于目标方法,那么将当前Advisor转换为MethodInterceptor,即通知转换为方法拦截器MethodInterceptor〔〕interceptorsregistry。getInterceptors(advisor);if(mm。isRuntime()){for(MethodInterceptorinterceptor:interceptors){将方法拦截器和切点对象封装成InterceptorAndDynamicMethodMatcher并加入拦截器链中interceptorList。add(newInterceptorAndDynamicMethodMatcher(interceptor,mm));}}else{interceptorList。addAll(Arrays。asList(interceptors));}}}}elseif(advisorinstanceofIntroductionAdvisor){IntroductionAdvisoria(IntroductionAdvisor)advisor;if(config。isPreFiltered()ia。getClassFilter()。matches(actualClass)){Interceptor〔〕interceptorsregistry。getInterceptors(advisor);interceptorList。addAll(Arrays。asList(interceptors));}}else{Interceptor〔〕interceptorsregistry。getInterceptors(advisor);interceptorList。addAll(Arrays。asList(interceptors));}}returninterceptorList;}复制代码
  通知链转换为拦截器链的过程概括如下。先从Advisor中将切点对象获取出来,并根据切点对象判断当前Advisor是否能够作用于目标方法;将能够作用于目标方法的Advisor封装成方法拦截器MethodInterceptor,并加入拦截器链。
  MethodInterceptor是一个接口,定义了方法拦截器,示例中使用的前置通知和后置通知对应的方法拦截器分别为MethodBeforeAdviceInterceptor和AspectJAfterAdvice,它们的关系可以用下面的类图表示。
  拦截器链获取到后,在JdkDynamicAopProxy的invoke()方法中还会再创建一个方法调用器ReflectiveMethodInvocation,其类图如下所示。
  通过类图可以知道,ReflectiveMethodInvocation中持有代理对象,目标对象,目标方法,目标方法参数,目标对象的Class对象,拦截器链,后续调用通知方法以及调用目标方法的逻辑的入口,就是ReflectiveMethodInvocation的proceed()方法。
  上面分析了AOP动态代理对象调用时,JdkDynamicAopProxy的invoke()方法中做的第一件事情,即将通知链中所有能够作用于当前目标方法的通知构建成拦截器链,并基于拦截器链生成方法调用器ReflectiveMethodInvocation。下面分析第二件事情,即调用ReflectiveMethodInvocation的proceed()方法,通过调用proceed()方法可以在调用目标方法的前后将通知的增强应用到目标方法上,下面分析一下整个调用流程,ReflectiveMethodInvocation的proceed()方法实现如下。currentInterceptorIndex用于指示当前需要调用的拦截器初始为1,每次使用前会先加1privateintcurrentInterceptorIndex1;publicObjectproceed()throwsThrowable{当拦截器都遍历完后,则调用目标方法if(this。currentInterceptorIndexthis。interceptorsAndDynamicMethodMatchers。size()1){调用invokeJoinpoint()方法来执行目标方法invokeJoinpoint()会调用AopUtils。invokeJoinpointUsingReflection(this。target,this。method,this。arguments),即通过反射执行目标方法returninvokeJoinpoint();}把拦截器获取出来ObjectinterceptorOrInterceptionAdvicethis。interceptorsAndDynamicMethodMatchers。get(this。currentInterceptorIndex);if(interceptorOrInterceptionAdviceinstanceofInterceptorAndDynamicMethodMatcher){InterceptorAndDynamicMethodMatcherdm(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;Classlt;?targetClass(this。targetClass!null?this。targetClass:this。method。getDeclaringClass());if(dm。methodMatcher。matches(this。method,targetClass,this。arguments)){调用各种通知对应的拦截器的invoke()方法returndm。interceptor。invoke(this);}else{returnproceed();}}else{拦截器是ExposeInvocationInterceptor时会调用到这里ExposeInvocationInterceptor的invoke()方法会先为当前线程保存方法调用器ReflectiveMethodInvocation然后再递归调用ReflectiveMethodInvocation的proceed()方法return((MethodInterceptor)interceptorOrInterceptionAdvice)。invoke(this);}}复制代码
  在ReflectiveMethodInvocation的proceed()方法中,会依次先调用所有拦截器的invoke()方法,最后才调用到目标方法,但是实际情况下,有一些通知是需要在目标方法执行后或者目标方法抛出异常时才执行,所以可以推测,调用了拦截器的invoke()方法不代表拦截器对应的通知逻辑会被执行,以及在拦截器的invoke()方法中会在某个时间点递归的调用回ReflectiveMethodInvocation的proceed()方法。
  为了方便理解,下面分别的以本示例中使用到的ExposeInvocationInterceptor,MethodBeforeAdviceInterceptor和AspectJAfterAdvice这三种拦截器进行说明。ExposeInvocationInterceptor的invoke()方法如下所示。publicObjectinvoke(MethodInvocationmi)throwsThrowable{MethodInvocationoldInvocationinvocation。get();为当前线程保存方法调用器,即保存ReflectiveMethodInvocationinvocation。set(mi);try{递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法returnmi。proceed();}finally{invocation。set(oldInvocation);}}复制代码
  ExposeInvocationInterceptor只是为当前线程保存了方法调用器的引用。MethodBeforeAdviceInterceptor的invoke()方法如下所示。publicObjectinvoke(MethodInvocationmi)throwsThrowable{前置通知的逻辑要先于目标方法执行,所以这里先执行前置通知的逻辑this。advice。before(mi。getMethod(),mi。getArguments(),mi。getThis());递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法returnmi。proceed();}复制代码
  MethodBeforeAdviceInterceptor的invoke()方法只要调用到了,那么对应的前置通知的逻辑就会被执行,这一点符合前置通知在目标方法执行前执行,前置通知逻辑执行完毕后,会再调用回ReflectiveMethodInvocation的proceed()方法,以便调用其它拦截器和目标方法。AspectJAfterAdvice的invoke()方法如下所示。publicObjectinvoke(MethodInvocationmi)throwsThrowable{try{递归调用ReflectiveMethodInvocation的proceed()方法来先执行其它拦截器或者目标方法returnmi。proceed();}finally{后置通知的逻辑要在目标方法执行后再执行,所以这里将后置通知的执行放在了finally中也表明就算目标方法或者其它拦截器执行时抛出异常,后置通知的逻辑也是会执行到的invokeAdviceMethod(getJoinPointMatch(),null,null);}}复制代码
  AspectJAfterAdvice的invoke()方法中,将后置通知逻辑的调用放在了finally中,所以后置通知的逻辑一定会等到其它通知和目标方法执行后再执行。
  那么到这里,方法调用器ReflectiveMethodInvocation调用拦截器和目标方法的流程已经形成了一个闭环,借助递归调用的特性,目标方法和拦截器都会被调用到,虽然目标方法的调用会在所有拦截器调用之后,但是目标方法的执行是会先于某些通知的执行的(比如后置通知)。
  最后还需要说明,在ReflectiveMethodInvocation的proceed()方法中,使用了currentInterceptorIndex字段来标识当前调用到了第几个拦截器,初始值为1,每次使用前先加1(即currentInterceptorIndex),那么拦截器在集合中的位置实际是会影响拦截器的invoke()方法的调用顺序,那么通过上面的源码分析,这个调用顺序的影响可以归纳如下。不同通知对应的拦截器在集合中的位置不会影响不同通知的调用顺序,比如前置通知逻辑的执行肯定会先于后置通知逻辑的执行;相同通知对应的拦截器在集合中的位置会影响相同通知的调用顺序,比如前置通知1在集合中的索引比前置通知2在集合中的索引小,那么前置通知1的逻辑的执行会先于前置通知2。三。时序图
  AOP动态代理对象执行方法时,调用时序图如下所示。
  总结
  SpringAOP的动态代理对象持有通知链和目标对象,那么在调用动态代理对象方法时,会先从通知链中找出能够作用于目标方法的Advisor,然后将每个符合条件的Advisor封装成MethodInvocation并加入集合,称MethodInvocation的集合为拦截器链,得到拦截器链后,会基于拦截器链创建方法调用器MethodInvocation,然后通过MethodInvocation的proceed()方法调用拦截器和目标方法的逻辑。

当众掌掴冯小刚!和周润发刘德华合照稳居C位,赵本山地位有多高12年前,赵本山对冯小刚的这一巴掌是彻底打出了自己在娱乐圈的地位,在座的嘉宾虽然惊到了,但也知道这是冯小刚该打。在2010年的华鼎奖颁奖典礼上,冯小刚作为颁奖嘉宾给赵本山颁奖,可在12月15日复刻先祖物品展示高束马尾,龟甲护面,光遇12月15日这天复刻的是预言家的水先知,也就是高马尾先祖,一起来看看水先知兑换建议以及物品攻略1面具(55蜡烛)水先知带来的是一个海龟面具,下面有一截胡子,同是米哈游的游戏,崩坏三和原神的口碑为什么差那么多?对二次元游戏熟悉的小伙伴应该都知道,崩坏三和原神这两款游戏都是米哈游旗下的产品,可风评却大不相同。原神口碑极好,很少有玩家骂,但是崩坏三却刚好相反,前两年口碑不错,最近一两年却像被荒野大镖客2每日任务攻略更新2022年12月15日常规任务1已烹饪调味的大型猎物肉03烹饪3次,记得添加调料2已剥下草原松鸡的皮03打猎3只剥皮3已制作一个马匹护理物品01制作一个4在自由模式活动中爆头杀死的玩家03左上方发出的活建设新农村老家生活公布Steam页面2023年发售3D模拟经营种田游戏老家生活公布Steam页面,预计2023年第三季度发售。老家生活由猫多游戏开发,益时光工作室发行,是一款种田人文模拟经营游戏,极具现代中国特色,展现中国新农村风魔域手游2王者来袭,拭目以待!在这个互联网游戏刚刚崛起的年代,涌现了很多经典的游戏,魔域端游就是在那个时候开始风靡的,一时之间风头无两。曾经的魔域端游以其精致华丽的造型快捷方便的新手引导新颖有趣的游戏玩法别出心美少女幸存者万物皆可美少女化欢迎关注,获取更多游戏评测资讯,入手与否不再犹豫喜欢的不妨点个赞唷()ArchmageGames工作室开发的roguelike弹幕射击游戏美少女幸存者,在吸血鬼幸存者爆火之后市面上梦幻西游69全服第一成就号,6件无级别3件超简易,成就超一万Hello大家好,我是浩仔!关注浩仔,每天给你带来最新梦幻西游游戏资讯!以下点评仅代表个人意见,不代表官方或平台立场。梦幻西游中有这样一群人,他们不爱PK不爱刷任务,就爱刷成就,被SP诸葛亮是输出放大器,搭配张角爆发高,搭配法正更稳定SP诸葛亮喜提增强,触发效果有了一定的提升,花席介绍5个SP诸葛亮的队伍,看看你有没有能用的。问一下谁同时有SP诸葛亮和天机四轮车,游戏的客服说SP诸葛亮可以依靠天机四轮车增加4概IP改编悬疑解谜游戏隐秘的角落将于2023年1月18日发售由ALUBA工作室研发的热门IP改编游戏隐秘的角落近日宣布将于2023年1月18日上线Steam正式版,价格信息尚未透露。在今年3月首曝后,暑期8月隐秘的角落也已在Steam上线过前列腺增生教你一方活血化瘀,利水散结前列腺增生属于比较高发的疾病,高发人群为50岁以上的男性患者。前列腺增生的患者主要症状有排尿困难小便无力尿频尿流细尿不尽等,此病在中医上属于癃闭。根据我多年的治疗经验来看,前列腺增
实体店的未来在哪里(4)图片来自头条实体店的机会在哪里,上篇主要讨论了服务的切入点,实际上没有一种方法能得到答案。随着互联网企业不断向下渗透,植入式,渐进式,裂变式借助OTO的机会进攻或者打入线下,线下实节后错峰出行!海南10个人少景美值得游玩的免费景点,你去过几个海南应该说是今年春节国内最火爆的旅游目的地之一,无论是餐饮住宿,还是机票景点门票都不便宜,如今人潮涌动的时候过去了,海南也开启了夏天到来之前气候最宜人,也是游玩性价比最高的时节,错法国国家公园六十年绿色发展之路光明图片视觉中国光明图片视觉中国环球视野法国是1992年联合国生物多样性公约的主要缔约方之一,2011年法国政府公布了包含六大方向的生物多样性国家战略,把生态文明建设纳入国家战略框云南昆明旅游,必打卡的八大景点,深度感受春城的独特魅力吧!被称为春城的昆明,四季如春,冬暖夏凉。昆明的美比较清淡,不喜欢的它的人会匆匆而过,对于喜欢它的人来说,移步便是景。1滇池,你来昆明,一定要去的地方就是滇池,云南省最大的淡水湖,有高火爆全网的ChatGPT,帮我们分析了长沙买房由美国人工智能实验室OpenAI发布推出的对话式大型语言模型ChatGPT,近段时间火爆了全网。这款全新聊天机器人模型,可以聊天写作编程翻译,甚至可以质疑和拒绝你的要求。不少和Ch从人脸到指纹,掌纹识别或将开启新时代该文首发于科技与金融杂志2022年10月刊2019年,科技与金融专访麦仑科技,当时企业成功推出了基于FVR技术的仙人掌AirWave智能系统。如今,这项掌脉技术已涵盖人手掌脉开锁人国际先进水平!中国原子能科学研究院成功自主研发AIE同位素光源封面新闻记者边雪不消耗任何燃料,也不需要电源就能自动发光,这款人人都想拥有的发光神器就是同位素光源(也叫原子灯)。作为核能之灯的一种,同位素光源可直接将放射性能量转换成光能。近日,兔年带娃逛上海动物园,千万要来这里看兔兔哦萌兔迎春,上海动物园推出了一系列兔年生肖活动。值得一提的是,在园内的儿童动物园,小朋友们可以参加兔年喂萌兔这一项目很有可能成为小朋友们玩转动物园的最爱哦!在现场,记者看到众多家长带颜骏凌追忆布拉泽维奇感谢他让我跳级进国奥被阿曼淘汰太可惜直播吧2月10日讯得知布拉泽维奇离世的消息后,在接受东方体育日报采访时,颜骏凌感谢了当年布拉泽维奇对他的提携。我那个时候能进国奥,其实就是老布把我招进队伍的,我比其他人小2岁,国奥往口红添加胡椒粉,不辣吗?这两年,化妆品行业彩妆品类的创新又遇到了瓶颈。纵观这两年的彩妆新品,大多数都是新瓶装旧酒,跨个界,换个包装,换个色彩,就说自己已经创新了。改变外观,用高颜值去吸引消费者,的确是优秀早春搭配,9种潮流的阔腿裤搭配,足够让你个性十足裤子种类千千万,当下唯有阔腿裤依旧流行于大街小巷,作为百搭的阔腿裤,搭配起来比较简单,也能轻松搭配出符合自己气质的风格。本期,糖果就和大家一起来聊聊9种常见的阔腿裤搭配,照着穿,你
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网