本节我们从ProxyFactory开始分析。该类有几个比较重要的方法——addAdvice、addAdvisor、getProxy,其中最后一个方法是我们本节的重点。前两个方法都是向ProxyFactory中成员变量advisors中加入成员,以便后面调用方法时实现拦截。
  这里,我们首先来了解前两个方法。在addAdvice中会调用到addAdvisor,而内部封装的advisor实际类型是DefaultPointcutAdvisor。如下图所示,这里将advice封装到DefaultPointcutAdvisor。这里我们默认只传入advice参数,在DefaultPointcutAdvisor中的成员变量pointcut默认为Pointcut.TRUE,也就是TruePointcut.INSTANCE,他比较特殊的是通过方法getClassFilter返回的是ClassFilter.TRUE,也就是TrueClassFilter.INSTANCE,通过方法getMethodMatcher返回的是MethodMatcher.TRUE,也就是TrueMethodMatcher.INSTANCE。通过他们的matches方法返回值都是true。接着,在addAdvisor方法中调用了addAdvisorInternal,该方法将入参advisor加入成员变量advisors中,然后将其中的值转换为数组赋给成员变量advisorArray(该变量用于后来在方法DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice中构建interceptorList列表),然后调用adviceChanged方法将成员变量methodCache中的值清空。
  下面,我们来到本节的重点ProxyFactory.getProxy,该方法并不复杂,只是返回一个代理对象,该代理对象除了实现了ProxyFactory.targetSource.getTargetClass所实现接口,另外实现了SpringProxy、Advised、DecoratingProxy这三个接口。
  在ProxyFactory.getProxy方法中调用了ProxyCreatorSupport.createAopProxy,该方法首先调用了方法activate,然后通过方法getAopProxyFactory获得成员变量aopProxyFactory,该成员变量在通过ProxyFactory调用时是DefaultAopProxyFactory的实例。关于ProxyCreatorSupport的另一个构造方法,入参为aopProxyFactory,调用是在ProxyFactoryBean.newPrototypeInstance中,我将放在以后来讲解。接下来调用了DefaultAopProxyFactory.createAopProxy,这里的入参就是我们的ProxyFactory,这点需要紧记,因为在后来的代理对象调用方法时会用到,这里的入参接着会传入到接下来构建的AopProxy实现类型中。在DefaultAopProxyFactory.createAopProxy方法中根据情况构造了ObjenesisCglibAopProxy或者JdkDynamicAopProxy。其中JdkDynamicAopProxy是AopProxy基于JDK的实现,而ObjenesisCglibAopProxy是基于Cglib的实现,他直接继承自CglibAopProxy。这里我只分析JdkDynamicAopProxy,大家有兴趣可以看一下ObjenesisCglibAopProxy,二者只是实现方式不同,大体流程是一致的。我们这里然后就调用了JdkDynamicAopProxy.getProxy,这里有一个参数是ClassUtils.getDefaultClassLoader(),该方法获取的是当前线程的ClassLoader,也就是默认的Launcher$AppClassLoader。然后通过方法AopProxyUtils.completeProxiedInterfaces填充了SpringProxy、Advised、DecoratingProxy这三个接口。而后通过Proxy.newProxyInstance方法构建了代理对象,注意,这里的最后一个入参就是我们这里的JdkDynamicAopProxy。
  下面,假设我们调用了返回的代理对象的某个方法,也就是说,我们将来到JdkDynamicAopProxy.invoke。不知道大家是否还记得,在JdkDynamicAopProxy.advised就是在DefaultAopProxyFactory.createAopProxy方法中的入参,也就是我们的ProxyFactory。让我们再回到JdkDynamicAopProxy.invoke方法。这里首先获得了advised.targetSource,也就是ProxyFactory.targetSource。这里的targetSource是在ProxyFactory的构造函数中将入参Object封装后的TargetSource,默认实现是SingletonTargetSource。接着调用了targetSource.getTarget,也就是获取到了我们这里的目标对象,也就是构造ProxyFactory时的入参Object。然后,获取到target的实际类型,调用了advised.getInterceptorsAndDynamicInterceptionAdvice,也就是方法AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice。该方法根据之前传入的advisor构建调用链。由于我们之前并没有调用该方法,因此,在AdvisedSupport.methodCache中并没有该方法的缓存值,然后调用了DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice来构造cached并将其将入到成员变量methodCache中。
  接下来,让我们来到DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice,该方法实现了将入参config中的Advisors转换为通过匹配后的MethodInterceptor列表,也就是真实方法调用前的拦截链。如下图所示,这里是getInterceptorsAndDynamicInterceptionAdvice的完整方法。
  1.首先调用GlobalAdvisorAdapterRegistry.getInstance获得DefaultAdvisorAdapterRegistry。这里我们需要注意的是在DefaultAdvisorAdapterRegistry的构造方法中已经调用registerAdvisorAdapter方法为其成员变量adapters加入了MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter这三个AdvisorAdapter。
  2.调用入参config.getAdvisors获取在ProxyFactory中配置的advisors。
  3.遍历ProxyFactory.advisors,一般来说,在ProxyFactory中加入的advisor是DefaultPointcutAdvisor,实现了接口PointcutAdvisor。因此,这里将advisor强转为PointcutAdvisor,获取其pointcut,紧接着调用Pointcut.getClassFilter,并调用ClassFilter.matches,判断目标类型是否与当前pointcut的ClassFilter相匹配,如果返回值为true,则继续调用PointcutAdvisor.getPointcut.getMethodMatcher获得MethodMatcher,接着判断其是否与目标类型的调用方法相匹配。如果当前调用方法确实是拦截点,就会调用DefaultAdvisorAdapterRegistry.getInterceptors将当前advisor转换为MethodInterceptor列表。
  这里我简单说一下PointcutAdvisor与Advisor。PointcutAdvisor实现接口Advisor,并且增加了方法getPointcut,返回的Pointcut就是用来判断当前执行的方法是否与当前PointcutAdvisor相匹配。而advisor的作用就是用来封装advice,其有一个方法是getAdvice。这里的DefaultAdvisorAdapterRegistry.getInterceptors方法我放到后面来讲解。
  4.将返回的MethodInterceptor数组加入到interceptorList中然后返回。
  这里我们首先来到DefaultAdvisorAdapterRegistry.getInterceptors。
  1.如果通过advisor.getAdvice获取的advice实现了接口MethodInterceptor,则直接将其加入到interceptors列表中。
  2.这里的成员变量adapters就是在DefaultAdvisorAdapterRegistry构造时就填充了MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter。接着遍历adapters并将满足条件的advice封装后加入到interceptors列表中。
  这里我们以MethodBeforeAdviceAdapter为例。在MethodBeforeAdviceAdapter.supportsAdvice方法中仅仅是判断入参advice是否实现了MethodBeforeAdvice接口,如果满足条件,则调用MethodBeforeAdviceAdapter.getInterceptor将advice强转为MethodBeforeAdvice并将其封装到MethodBeforeAdviceInterceptor中。
  到此为止,我们就构造好了AdviceChain,接下来,来到本节的最后一个重点——构造ReflectiveMethodInvocation,并调用其proceed方法。在构造ReflectiveMethodInvocation时,其最后一个入参就是我们刚刚构造好的chain。接下来,我们来到ReflectiveMethodInvocation.proceed。这里的链式调用很类似web应用中的过滤器,可能spirng团队也是经常写web架构的。
  1.这里首先判断当前调用是否已经将所有的调用链完成,如果已经完成,则调用invokeJoinpoint,触发真实要执行的方法。大家可能比较疑惑,这里为什么是interceptorsAndDynamicMethodMatchers.size() - 1,因为这里的currentInterceptorIndex是从-1开始的,如果从0开始的话,那么,显然就没有后面的 - 1。
  2.从interceptorsAndDynamicMethodMatchers列表中获取值,然后调用其invoke方法。这里我把MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor都讲解一下。
  如果这里的MethodInterceptor实际类型是MethodInterceptor,那么调用了MethodBeforeAdviceInterceptor.invoke,注意,这里的入参是ReflectiveMethodInvocation,也就是说,这里在调用了advice.before后,接着调用了ReflectiveMethodInvocation.proceed,接着来到ReflectiveMethodInvocation.proceed。
  然后假设这里的MethodInterceptor实际类型是AfterReturningAdviceInterceptor,这里直接就调用了ReflectiveMethodInvocation.proceed,在其调用完成后,调用了advice.afterReturning。
  接着,我们假设这里的MethodInterceptor实际类型是ThrowsAdviceInterceptor,这里直接调用了ReflectiveMethodInvocation.proceed,不过,这里添加了异常捕获如果获取到对应的Method,则通过invokeHandlerMethod调用捕获异常的方法。然后继续将异常抛出。
  可以说,这里很好的使用的递归的思路,实现了拦截器的链式调用。
  到这里,本节的内容就结束了。尽管内容洋洋洒洒,但是当你调试代码的时候,会发现其实并没有多少东西。希望大家在看本文的时候尽量结合源码调试,以加深理解。如果有疑问或有相关问题探讨,欢迎大家留言。

Spring之AOP流程解析(ProxyFactory)的更多相关文章

  1. spring的AOP个人理解和使用

    1什么是AOP:AOP是面向切面编程,也就是说面向某个功能模块编程,典型的应用就是Spring的声明式事务, Spring的AOP事务解析: 在以前的事务管理是要融合在逻辑代码中的,在逻辑代码中决定事 ...

  2. Spring中AOP相关的API及源码解析

    Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...

  3. Spring源码分析之AOP从解析到调用

    正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...

  4. Spring中AOP相关源码解析

    前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...

  5. spring加载bean流程解析

    spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步.不过作为朝夕相处的框架,我们必须得明白一个问题就是sprin ...

  6. Spring的AOP配置文件和注解实例解析

    1.1           Spring的AOP配置文件和注解实例解析 AOP它利用一种称为"横切"的技术,将那些与核心业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减 ...

  7. Spring Boot AOP解析

    Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspec ...

  8. spring AOP XML解析

    <aop:config> 标签的解析: <bean id="loggingAspect" class="com.zhuguang.jack.aop.as ...

  9. Spring Aop(十一)——编程式的创建Aop代理之ProxyFactory

    转发地址:https://www.iteye.com/blog/elim-2397388 编程式的创建Aop代理之ProxyFactory Spring Aop是基于代理的,ProxyFactory是 ...

随机推荐

  1. Linux scp 命令卡住的原因

    When transferring large files(for example mksysb images) using scp through a firewall, the scp conne ...

  2. 解决Spring boot中读取属性配置文件出现中文乱码的问题

    问题描述: 在配置文件application.properties中写了 server.port=8081 server.servlet.context-path=/boy name=张三 age=2 ...

  3. ubuntu频繁出现 安装包依赖关系

    折腾了一下午,还差点重装一次,最后记下解决办法,引以为戒! 第一步,备份官方的默认源 避免自己手贱操作失误,重装系统太费时间 cp /etc/apt/sources.list /etc/apt/sou ...

  4. 派多个订单给一个司机,拒单是同一订单id

    问题:多次派单给一个司机,发现多个拒单请求是同一个订单id的. 原因:来单页面是SingleTask, 并且没有重写onNewIntent, 而倒计时结束拒单的时候会弹窗提示,只有点了确认按钮才会把当 ...

  5. go web开发(gin&gorm) 之DB配置及DAO的基本使用

    转载请注明出处: https://www.cnblogs.com/funnyzpc/p/9501376.html ```   我先闲扯下,前天(也就是2018年11月16号)的某个时候,忽然有人在QQ ...

  6. Linux虚拟机搭建本地yum源

    Yum本地源的配置 本教程是在虚拟机里安装Red Hat Enterprise Linux 7 ,以其为例使用iso文件进行Yum本地源的配置.所使用的软件如下: (1)虚拟机:Vmware work ...

  7. Python-写文件

    写文件需要三步:打开文件写入内容关闭文件 写入内容一般要选择打开的模式:f = open('out.txt','w')此处的w就是writing,代表以写入文件的模式打开,原文件里的内容会被新写入覆盖 ...

  8. TypeScript专题-Static和使用技巧

    class People { static _name: string; print() { //alert(this.name);// 编译不通过,doex not exist on type Pe ...

  9. [LeetCode] Score of Parentheses 括号的分数

    Given a balanced parentheses string S, compute the score of the string based on the following rule: ...

  10. HTML文档结构

    下面对HTML文档结构进行一 一解释: 1.文档声明:既不是元素,也不是注释: 代码格式:<! DOCTYPE html> 注:必须写在HTML文档的第一行 原因:告诉浏览器使用哪个版本的 ...