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

详细分析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() 方法作为入口开始分析。public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {     Object oldProxy = null;     boolean setProxyContext = false;      TargetSource targetSource = this.advised.targetSource;     Object target = null;      try {         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {             // 不会将通知作用在equals()方法,除非目标对象实现的接口中定义了equals()方法             return equals(args[0]);         }         else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {             // 不会将通知作用在hashCode()方法,除非目标对象实现的接口中定义了hashCode()方法             return hashCode();         }         else if (method.getDeclaringClass() == DecoratingProxy.class) {             return AopProxyUtils.ultimateTargetClass(this.advised);         }         else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {             // 不会将通知作用于Advised接口或者其父接口中定义的方法             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);         }          Object retVal;          if (this.advised.exposeProxy) {             oldProxy = AopContext.setCurrentProxy(proxy);             setProxyContext = true;         }          // 获取目标对象         target = targetSource.getTarget();         // 获取目标对象的Class对象         Class<?> targetClass = (target != null ? target.getClass() : null);          // 将通知链中能够作用于当前方法的通知组装成拦截器链         List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);          if (chain.isEmpty()) {             Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);             // 如果拦截器链是空,那么直接调用目标对象方法             // AopUtils.invokeJoinpointUsingReflection()最终会调用到method.invoke(target, args)             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);         }         else {             // 创建方法调用器MethodInvocation,实际为ReflectiveMethodInvocation             // 创建ReflectiveMethodInvocation时传入的参数依次为:代理对象,目标对象,目标方法,目标方法参数,目标对象的Class对象,拦截器链             MethodInvocation invocation =                     new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);             // 调用方法调用器的proceed()方法,开始进入调用各个通知方法和目标方法的递归流程             retVal = invocation.proceed();         }          Class<?> returnType = method.getReturnType();         if (retVal != null && retVal == target &&                 returnType != Object.class && returnType.isInstance(proxy) &&                 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {             retVal = proxy;         }         else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {             throw new AopInvocationException(                     "Null return value from advice does not match primitive return type for: " + method);         }         return retVal;     }     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(),如下所示。public List getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {     MethodCacheKey cacheKey = new MethodCacheKey(method);     // 先根据Method从缓存中拿拦截器链,缓存中没有时会去生成并缓存起来     List cached = this.methodCache.get(cacheKey);     if (cached == null) {         // 实际调用到DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice()方法来生成拦截器链         cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(                 this, method, targetClass);         this.methodCache.put(cacheKey, cached);     }     return cached; } 复制代码
  每个目标方法对应的拦截器链在生成后都会被缓存,所以会先从缓存中拿拦截器链,缓存中没有时会去调用DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice() 方法来生成拦截器链,getInterceptorsAndDynamicInterceptionAdvice() 方法实现如下。public List getInterceptorsAndDynamicInterceptionAdvice(         Advised config, Method method, @Nullable Class<?> targetClass) {      AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();     // 先从ProxyFactory中将通知链获取出来     Advisor[] advisors = config.getAdvisors();     List interceptorList = new ArrayList<>(advisors.length);     Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());     Boolean hasIntroductions = null;      for (Advisor advisor : advisors) {         // 因为Advisor接口有两个子类接口,分别是PointcutAdvisor和IntroductionAdvisor         if (advisor instanceof PointcutAdvisor) {             // 处理PointcutAdvisor,示例中使用的都是PointcutAdvisor             PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;             if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {                 // 获取切点对象,这里的mm类型为AspectJExpressionPointcut                 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();                 boolean match;                 if (mm instanceof IntroductionAwareMethodMatcher) {                     if (hasIntroductions == null) {                         hasIntroductions = hasMatchingIntroductions(advisors, actualClass);                     }                     // 判断当前Advisor是否能够作用于目标方法                     match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);                 }                 else {                     // 判断当前Advisor是否能够作用于目标方法                     match = mm.matches(method, actualClass);                 }                 if (match) {                     // 如果当前Advisor能够作用于目标方法,那么将当前Advisor转换为MethodInterceptor,即通知转换为方法拦截器                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);                     if (mm.isRuntime()) {                         for (MethodInterceptor interceptor : interceptors) {                             // 将方法拦截器和切点对象封装成InterceptorAndDynamicMethodMatcher并加入拦截器链中                             interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));                         }                     }                     else {                         interceptorList.addAll(Arrays.asList(interceptors));                     }                 }             }         }         else if (advisor instanceof IntroductionAdvisor) {             IntroductionAdvisor ia = (IntroductionAdvisor) advisor;             if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {                 Interceptor[] interceptors = registry.getInterceptors(advisor);                 interceptorList.addAll(Arrays.asList(interceptors));             }         }         else {             Interceptor[] interceptors = registry.getInterceptors(advisor);             interceptorList.addAll(Arrays.asList(interceptors));         }     }      return interceptorList; } 复制代码
  通知链转换为拦截器链的过程概括如下。先从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,每次使用前会先加1 private int currentInterceptorIndex = -1;  public Object proceed() throws Throwable {     // 当拦截器都遍历完后,则调用目标方法     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {         // 调用invokeJoinpoint()方法来执行目标方法         // invokeJoinpoint()会调用AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments),即通过反射执行目标方法         return invokeJoinpoint();     }      // 把拦截器获取出来     Object interceptorOrInterceptionAdvice =             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {         InterceptorAndDynamicMethodMatcher dm =                 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;         Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());         if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {             // 调用各种通知对应的拦截器的invoke()方法             return dm.interceptor.invoke(this);         }         else {             return proceed();         }     }     else {         // 拦截器是ExposeInvocationInterceptor时会调用到这里         // ExposeInvocationInterceptor的invoke()方法会先为当前线程保存方法调用器ReflectiveMethodInvocation         // 然后再递归调用ReflectiveMethodInvocation的proceed()方法         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);     } } 复制代码
  在ReflectiveMethodInvocation的proceed() 方法中,会依次先调用所有拦截器的invoke() 方法,最后才调用到目标方法,但是实际情况下,有一些通知是需要在目标方法执行后或者目标方法抛出异常时才执行,所以可以推测,调用了拦截器的invoke() 方法不代表拦截器对应的通知逻辑会被执行,以及在拦截器的invoke() 方法中会在某个时间点递归的调用回ReflectiveMethodInvocation的proceed() 方法。
  为了方便理解,下面分别的以本示例中使用到的ExposeInvocationInterceptor,MethodBeforeAdviceInterceptor和AspectJAfterAdvice这三种拦截器进行说明。ExposeInvocationInterceptor的invoke() 方法如下所示。public Object invoke(MethodInvocation mi) throws Throwable {     MethodInvocation oldInvocation = invocation.get();     // 为当前线程保存方法调用器,即保存ReflectiveMethodInvocation     invocation.set(mi);     try {         // 递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法         return mi.proceed();     }     finally {         invocation.set(oldInvocation);     } } 复制代码
  ExposeInvocationInterceptor只是为当前线程保存了方法调用器的引用。MethodBeforeAdviceInterceptor的invoke() 方法如下所示。public Object invoke(MethodInvocation mi) throws Throwable {     // 前置通知的逻辑要先于目标方法执行,所以这里先执行前置通知的逻辑     this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());     // 递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法     return mi.proceed(); } 复制代码
  MethodBeforeAdviceInterceptor的invoke() 方法只要调用到了,那么对应的前置通知的逻辑就会被执行,这一点符合前置通知在目标方法执行前执行,前置通知逻辑执行完毕后,会再调用回ReflectiveMethodInvocation的proceed() 方法,以便调用其它拦截器和目标方法。AspectJAfterAdvice的invoke() 方法如下所示。public Object invoke(MethodInvocation mi) throws Throwable {     try {         // 递归调用ReflectiveMethodInvocation的proceed()方法来先执行其它拦截器或者目标方法         return mi.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() 方法调用拦截器和目标方法的逻辑。
阴阳师6周年情报分析,须佐和SP荒实锤,还有茨林典藏关于阴阳师6周年庆典可能会上线的一系列福利,阿诚其实专门做过一期分析,并在分析文章里明确提到了须佐之男SP荒和典藏皮肤的相关线索。但由于分析是建立在已有的情报线索的基础上,并且时间传奇手游排行榜前10名手游推荐如何快速玩转传奇最近一直有兄弟私信我冰雪悍刀行怎么玩,今天我就来教大家如何快去玩转传奇,有需求的兄弟一定要坚持看完呀首先进游的福利,背包里的大小什么券领取后直接拉满,全属性加10个点,点击右下角的穿过连绵不绝有如蚂蚁般大小的大都市,找寻回家的路小眷灵Tinykin是由Splashteam制作,tinyBuild发行的一款3D平台跳跃收集马拉松冒险游戏。整体游玩下来,本作很自然的会让笔者联想到老任家的皮克敏(玩法)和纸片马赛季末上分英雄干将仅排T2,周瑜能排T1,而能抗能奶的他为唯一T0哈喽,大家好!我是老张。在这个赛季,很多玩家都为了上分十分头疼,尤其是中路的位置,你选择一个比较弱势的英雄前期队友基本上没法玩的,好不容易凭借着发育的优势将自身的经济成形了,但是却银河战舰都没了?S12四大赛区花钱最多俱乐部现状游戏马蹄铁原创欧洲VIT这支欧洲LEC战队在今年是进行了一次彻底重组,Perkz,Carzzy,Haru等等具有高知名度的选手纷纷加入。季中之后,他们甚至从LPL签下了Bo,不过由传奇手游新手入门攻略大家好,我是刘大大。我来了,今天给大家分享一下热血传奇的新手攻略,喜欢的朋友可以点点关注。今天我就以一款传奇游戏封月传奇为例子举例,首先在玩家刚进入游戏时,往往因为等级不足,装备不电竞破局下沉出海造星NFT与线下衍生采写王红霞编辑万天南即使你不玩电竞,也不看比赛,但它还是会离你的生活越来越近,或许是某个线上视频或许在某个线下场馆。比如,上个月刚在杭州内测的腾讯电竞酒店7月底收官的电竞实训综艺战鸡身上最脏的三个器官,到处都是寄生虫,还有很多人抢着吃如今这个社会,随着生活越来越好,相信大家对食物的质量的要求也越来越高,大家对美食也是爱不释手,在日常我们的生活中都离不开吃一些鸡鸭鱼肉,特别是鸡肉在我们平时吃的食物是最多的一种了。为何那么多心血管事件,都多发于早晨?做好6点,避免血管失控夏季早晨5点左右就天亮了,在公园或广场能看到很多中老年人晨练,虽然早晨凉爽,晨练有益于机体健康。不过,最让人们扎心的是,清晨是心脑血管疾病高发期,尤其是缺血性脑卒中和急性心梗等,所食用香椿会致癌?传言称,香椿(芽)中含有大量的亚硝酸盐物质,简单食用就会中毒致癌。其实,硝酸盐和亚硝酸盐广泛存在于自然界的水和土壤中,所以大部分蔬菜都会含有一定量的硝酸盐和亚硝酸盐物质,只是含量不手脚冰凉是什么原因造成的?手脚冰凉,大家对这种现象并不陌生,尤其是女性和老年人。即使是在炎热的夏天,他们也觉得自己的手和脚没有温度,这种情况一定要引起重视!下面给大家讲一下手脚冰凉到底是什么原因造成的?1情
俗话说精足不思淫,气足不思食,神足不思眠,到底啥意思?许多的俗语表面上看含义很深刻,但只要深入去了解,会发现每一句话都有其重要的意义,暗含的道理可以起到警醒,指导作用。有的人经常说精足不思淫气足不思食神足不思眠,究竟这几句话代表什么应周六立春,咬春以迎春,你知道要咬什么吗?钱江晚报小时新闻记者杨茜通讯员张弛2月4日,周六,立春。立春为二十四节气之首,预示着寒冬已尽,春回大地,自此万物更新,生机勃勃。虽说过了立春,气温日照降雨均开始趋于上升增多,但我国6个流传很广的假养生之道,你以为是在养生,其实会有害健康现在,随着我们日常生活水平的不断提高,也出现了越来越多的人把目光关注到自己的身体健康上。没有一个好的身体,再多的钱也没有意义。现在,日常养生早已逐渐成为一种趋势,越来越多的人也开始今天全世界都在看的新闻2023。2。2普京俄罗斯首要任务是消除乌军炮击俄的可能性俄罗斯总统普京2月1日召开视频会议,重点讨论恢复在俄乌边境地区因紧急情况而遭到破坏的住宅和基础设施。普京表示,俄罗斯的首要任务是消除乌军炮Intel发布13代酷睿核显新驱动没打鸡血修复游戏崩溃来源快科技Intel显卡的硬件架构很先进,但在驱动优化上一直被人诟病,这也是Intel这一年来发力的地方,之前推出的Arc显卡驱动多次打鸡血,DX9性能累计提升了40以上,日前又给吃大蒜能远离痛风?医生少吃这2类食物,或能杜绝尿酸飙升很多人都有高尿酸的症状,如果没有及时的控制好自己的尿酸,时间一长,就会在关节处形成尿酸盐晶体,从而引发痛风,造成关节疼痛,甚至会造成关节变形,从而影响走路。如果病情发展到一定程度,1。35亿巨星5年里身价暴跌,足迹曾遍布四大联赛,本赛季0球0助攻在足球世界中,球员的身价往往代表了球员的价值,但是有的时候过高的身价也会成为前进的障碍,为球员增加巨大的心理负担。今年30岁的巴西球星库蒂尼奥目前效力于英超阿斯顿维拉队,做为一名技GIF足坛十大离谱空门不进!没有一个省心的!说到空门不进,我想大家脑海里都有一个经典的名场面,那么接下来,我给大家盘点10个最离谱的空门不进!看看有没有你们想的那个!10快乐永远属于斯特林,这球想必应该也就是常规操作了吧!9库里29分10板勇士加时惜败,森林狼三人砍20勇士客场加时114119不敌森林狼。鲁迪戈贝尔缺阵。追梦空切上篮,汤普森两罚全中,勇士40开局。爱德华兹拉塞尔回敬一波90!汤普森普尔维金斯连中三分,勇士2118反超。爱德华兹奥斯权力的游戏三傻的另外两部电影一部长达8年的电视剧,不但备受好评红遍全球,更是造就了一批年轻的演员。不得不说史诗奇幻题材的权力的游戏,真的是一部非常棒的电视剧。在这部剧中最大的赢家非饰演龙妈的艾米莉亚克拉克莫属黑神话兔年彩蛋曝光!国产游戏仍然值得期待!1月16日,游戏科学(简游科)如约发布了一段黑神话的贺岁影片,历时黑神话破圈已有3年。2020年8月国产游戏黑神话悟空13分钟实机演示一经公布,迅速抢占微博b站等主流媒体头条,游戏