本节我们从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是 ...
随机推荐
- md
> 引用# 一级标题## 二级标题,总共六级标题 - ul + li + li 1. 1232. 456 [链接](http://www.baidu.com) ![图片](http://plac ...
- Kafka提交offset机制
在kafka的消费者中,有一个非常关键的机制,那就是offset机制.它使得Kafka在消费的过程中即使挂了或者引发再均衡问题重新分配Partation,当下次重新恢复消费时仍然可以知道从哪里开始消费 ...
- pycharm中range的应用
v = range(100) for item in v: print (item) #输出结果是0 1 2 3 ......99 这是在python3中实现的,python2中不一样 下面是一个从大 ...
- ImCash:韩国最大交易所遭遇至暗时刻:2018年亏损1.8亿美元
Bithumb上个月遭到黑客攻击,随后要求用户小心存款,该公司报告称损失1.8亿美元(合2050亿韩元). 据<韩国时报>(Korea Times)报道:受到熊市影响,数字货币交易所实际交 ...
- WebStorm重复代码快捷表达
一,问题 平时使用WebStorm时需要很多引用js,重复代码比较多,每次都要写很多次同样的代码,那么如何通过几个快捷键来简单的写出重复代码呢? 问题具体描述: 每次都要写两个script的重复: & ...
- 使用anaconda创建tensorflow环境后如何在jupyter notebook中使用
在以下目录中 C:\Users\UserName\AppData\Roaming\jupyter\kernels\python3 打开kernel.json文件,将python.exe文件的路径修改至 ...
- 意识科学初步:David Chalmers的简单问题与困难问题
这是第一篇关于意识科学的内容.主要谈一下阅读大卫查莫斯的几篇论文的一些观点和思考. 论文作者简介(摘自wiki): David John Chalmers (born 20 April 1966) i ...
- 快速安装puppeteer (跳过安装Chromium)
npm i --save puppeteer --ignore-scripts
- vue 时间戳 转 日期
<text style="padding-right: 10px; color: #333; font-size: 28px" slot="value"& ...
- [LeetCode] All Nodes Distance K in Binary Tree 二叉树距离为K的所有结点
We are given a binary tree (with root node root), a target node, and an integer value K. Return a li ...