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

详细分析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() 方法调用拦截器和目标方法的逻辑。
王者荣耀排位一楼抢什么英雄最坑?拿姜子牙还能忍,抢他就六分投,你觉得呢?在王者荣耀中,排位赛玩法是一个玩家很喜欢的模式,因为这个模式玩家可以用它来上分,达到相应的段就可以获得奖励而且段位越高就往往代表着实力越高,是王者荣耀里一种荣誉的象征。我们在打排位同时拥有破甲弓和百穿铭文,物理穿透是先计算哪一个?这个问题换个思路就很好明白了,先说出答案,物理穿透是先计算百穿铭文,再计算破甲弓的。也就是说穿透属性都是先计算固定值,再计算百分比值。我们用一个简单的例子来说明,当李白拥有百穿加暗奥运会的哪些项目不代表世界最高水平?那必须是乒乓球啊,刘国梁张继科等球员世界冠军拿到手软,愣是拿不着一个全国冠军!(甚至打个奥运会都不兴奋)尽管奥运会是目前全球范围内综合性,影响程度最高的体育盛会,但其实它的很多参赛科比生涯场均24。9分,不到25分。命中率又低!为什么还会被称为超级得分手呢?科比生涯场均得分24。9分历史第13名,其实已经算联盟的翘楚,但与科比本身的得分造诣并不相符,科比在得分方面值得更好的名次,究科比场均得分与实际能力不符的原因有二其一起步相对较晚的在NBA历史上有没有单赛季同时拿到3个MVP得分王以及赛季双一阵的球员?单赛季常规赛MVP全明星赛MVP总决赛MVP同时包揽得分王最佳阵容一阵最佳防守阵容一阵这样的神迹,当然只有篮球之神才能做到。他就是迈克尔乔丹。199798赛季,是一个注定要被历史铭亚运会中国女排最终参赛名单得到确认,比赛期间你会格外关注哪些队员的表现?昨天参加印尼雅加达亚运会的中国代表团公布了参加亚运会的终极名单,中国女排的名单没有再调整,14人名单是主攻朱婷张常宁李盈莹刘晓彤副攻颜妮袁心玥胡铭媛二传丁霞刁琳宇接应龚翔宇曾春蕾杨现在微商卖的高音质u盘,真的在车上放,音质会有提高吗?这类U盘与普通U盘音色还真不一样。但是此类U盘宣传上有些夸大,例如黑胶芯片无损音频6D音效等纯属夸大宣传。例如黑胶的概念是唱片,U盘就是存储数据的一个载体,数字信号只需要存储01即想买个音质好大电池或者续航好一点的手机,价格在千元左右,有哪些推荐?感谢邀请我是科技数码随时答,很高兴能回答这个问题音质好大电池或者续航好一点的手机,价格在千元左右,有哪些推荐?这个是可以有的,目前来说的千元机市场是百家争鸣的景象,如果想要音质好的如果你只能买一支口红,你买哪个?如果只能买一支口红,当然是没有的那支啦!因为现有的口红每个颜色很少会用完GivenchyMacTFYSLDior香奈儿兰蔻雅诗兰黛阿玛尼娇兰等大牌口红永远是每一个爱美女性化妆台上必mac的口红有没有必须要入手的几支?MAC作为口红届的扛把子,色号之多让人难以想象,基本上只有你想不到的颜色,没有MAC出不来的颜色。他们家的口红性价比也比较高,基本上150左右的价格就都能买到了,而且显色度滋润哑光为什么感觉现在的中国家庭,特别是40岁以下的家庭基本不看电视?因为工作忙,还需要学习增加自己的工作能力,还有要照顾孩子。还有一部分人是打游戏!一是工作忙,二是电视节目单一,手机可以替代电视。一,电视节目千篇一律。抗日神剧,娱乐至上,漫天广告,
庾澄庆与47岁老婆腻歪依偎!女方戴大金链好贵气,一颦一笑似少女饿了吗?戳右边关注我们,每天给您送上最新出炉的娱乐硬核大餐!11月19日,有台媒曝光歌手庾澄庆与老婆张嘉欣同框的照片,引发外界热议。照片中,61岁的庾澄庆穿着白色T恤,戴着红色棒球将军丈夫膝下无子,妻子劝丈夫再娶,后来生下4个科学家孙立人的一生育有两子两女。这四个孩子的成就是显著的。后来都成为了有名的科学家。不过,孙立人的这四个孩子,并不是他的妻子张晶英所生。原来,张晶英无法生育,为了能让孙家有后,张晶英这才空有凌云志却不识人,徽宗活该下场凄惨!(宋徽宗时代第2卷150)第二部时代拼图人人都想证明自己,帝王也不例外。所不同的是,普通人证明自己,以个人的人生为代价帝王证明自己,以整个王朝亿万生灵的人生为代价。这看上去很不公平,可这就是人类社会的本质。小人得志莫猖狂谈古论今不荒唐,小人得志莫猖狂。善恶到头终有报,人间正道是沧桑。有君子的地方就有小人,有小人的地方就有江湖,君子是正义的化身,小人是邪恶的代表,正邪之争,善恶之别,虽说正义最终战胜铁血贵州省长军阀出身的周西城你眼中的军阀是什么形象呢?是那种烧杀淫掠,无恶不作的,还是那种风度翩翩的影视剧中的形象。今天我们来说一下军阀出身的贵州省长周西城。年轻帅气的周西成周西城一九一一年参军,一九二六年被互联网增长策略的底层逻辑有一个奇怪的现象。互联网公司极其重视战略定位,但很少使用定位理论。我们从来没有听到中国互联网前10名头部公司创始人,公开使用定位理论来介绍自己的公司。我是谁?消费理由?信任状?那互密县供销社为何建社不到一年即开始整顿1951年下半年到1952年上半年,密县供销社进入了快速发展期,然而一年以后的1952年7月即进行了整顿,其中的原因是什么呢?1951年夏季,河南省合作社召开第一次社员代表会议,作有把iPhone12mini当主力机的吗?12mini一出就买了,一直用到现在快两年了,从4S到5S,iphone6到12mini,中间也使用过华为mate10,mate20,但还是喜欢用小屏手机,钟爱小屏手机的完美掌控感补肾固原的长寿穴涌泉(井穴)别名地冲穴义体内肾经的经水由此外涌而出。名解1)涌泉。涌,外涌而出也。泉,泉水也。涌泉名意指体内肾经的经水由此外涌而出体表。本穴为肾经经脉的第一穴,它联通肾经的体内体表有高血压的人,需要控制心率吗,控制在多少比较好呢?有位患高血压的朋友问华子,他的心率每分钟80多次,是不是太快了,要不要控制一下?有什么方法可以控制,控制到什么程度比较好呢?华子说,有高血压的人应当关注自己的心率,如果心率超过每分字节跳动上线番茄畅听音乐版,听歌可赚金币提现记者肖芳据界面新闻了解,字节跳动新上线一款音乐App番茄畅听音乐版,该App为番茄畅听的衍生版,主要聚焦音乐流媒体服务,而番茄畅听集合了音乐小说广播相声评书等多种流媒体服务。在产品