本节我们从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个人理解和使用
1什么是AOP:AOP是面向切面编程,也就是说面向某个功能模块编程,典型的应用就是Spring的声明式事务, Spring的AOP事务解析: 在以前的事务管理是要融合在逻辑代码中的,在逻辑代码中决定事 ...
- Spring中AOP相关的API及源码解析
Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...
- Spring源码分析之AOP从解析到调用
正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...
- Spring中AOP相关源码解析
前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...
- spring加载bean流程解析
spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步.不过作为朝夕相处的框架,我们必须得明白一个问题就是sprin ...
- Spring的AOP配置文件和注解实例解析
1.1 Spring的AOP配置文件和注解实例解析 AOP它利用一种称为"横切"的技术,将那些与核心业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减 ...
- Spring Boot AOP解析
Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspec ...
- spring AOP XML解析
<aop:config> 标签的解析: <bean id="loggingAspect" class="com.zhuguang.jack.aop.as ...
- Spring Aop(十一)——编程式的创建Aop代理之ProxyFactory
转发地址:https://www.iteye.com/blog/elim-2397388 编程式的创建Aop代理之ProxyFactory Spring Aop是基于代理的,ProxyFactory是 ...
随机推荐
- Linux scp 命令卡住的原因
When transferring large files(for example mksysb images) using scp through a firewall, the scp conne ...
- 解决Spring boot中读取属性配置文件出现中文乱码的问题
问题描述: 在配置文件application.properties中写了 server.port=8081 server.servlet.context-path=/boy name=张三 age=2 ...
- ubuntu频繁出现 安装包依赖关系
折腾了一下午,还差点重装一次,最后记下解决办法,引以为戒! 第一步,备份官方的默认源 避免自己手贱操作失误,重装系统太费时间 cp /etc/apt/sources.list /etc/apt/sou ...
- 派多个订单给一个司机,拒单是同一订单id
问题:多次派单给一个司机,发现多个拒单请求是同一个订单id的. 原因:来单页面是SingleTask, 并且没有重写onNewIntent, 而倒计时结束拒单的时候会弹窗提示,只有点了确认按钮才会把当 ...
- go web开发(gin&gorm) 之DB配置及DAO的基本使用
转载请注明出处: https://www.cnblogs.com/funnyzpc/p/9501376.html ``` 我先闲扯下,前天(也就是2018年11月16号)的某个时候,忽然有人在QQ ...
- Linux虚拟机搭建本地yum源
Yum本地源的配置 本教程是在虚拟机里安装Red Hat Enterprise Linux 7 ,以其为例使用iso文件进行Yum本地源的配置.所使用的软件如下: (1)虚拟机:Vmware work ...
- Python-写文件
写文件需要三步:打开文件写入内容关闭文件 写入内容一般要选择打开的模式:f = open('out.txt','w')此处的w就是writing,代表以写入文件的模式打开,原文件里的内容会被新写入覆盖 ...
- TypeScript专题-Static和使用技巧
class People { static _name: string; print() { //alert(this.name);// 编译不通过,doex not exist on type Pe ...
- [LeetCode] Score of Parentheses 括号的分数
Given a balanced parentheses string S, compute the score of the string based on the following rule: ...
- HTML文档结构
下面对HTML文档结构进行一 一解释: 1.文档声明:既不是元素,也不是注释: 代码格式:<! DOCTYPE html> 注:必须写在HTML文档的第一行 原因:告诉浏览器使用哪个版本的 ...