Spring-AOP源码分析随手记(二)
这次来分析下切面的执行过程。
1.怎么看?
怎么开始看源码呢?就直接从被增强的方法调用那里打断点,看看怎么执行的:
然后就来到了这:
2.初步分析
里面有段:
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
就是上篇文章讲到的注解配置暴露代理对象,放到AopContext的ThreadLocal里去,之后就可以随时用 AopContext.currentProxy())取到代理对象。
接下来有段重要的:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
获取拦截器链,就是把这次相关的增强器转化成拦截器获取出来
然后:
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
这里就是判断拦截器链有没有东西,如果是空的就直接通过反射调用,不是空就进行else逻辑了,那else是重点了,即invocation.proceed();
3.invocation.proceed()
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
第一个if是递归的终止条件,明显是根据下标进行终止的条件
后面进行前++,又调用了
((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
然而这里面是:
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
又跑回proceed方法去了,递归。
第二次来到的时候下标就是0了(第一次是-1,默认的),前++为1的下标的话,取出来的东西继续调用invoke发现进的是AspectJAfterThrowingAdvice的invoke了(第一次是ExposeInvocationInterceptor的invoke,记录下MethodInvocation供后面执行链获取)
这个AspectJAfterThrowingAdvice的invoke的源码如下(不一样):
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
继续递归,不过这次把调用链try起来了,出异常就走异常增强通知invokeAdviceMethod
继续debug,又是递归到invoke,但这次是AfterReturningAdviceInterceptor的:
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
提一下啊,递归是栈结构!所以我们先看到了异常调用代码和返回通知代码!
继续递归proceed到了后置通知AspectJAfterAdvice类的invoke:
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
看到没,后置通知类的invokeAdviceMethod调用是用的finally,所以后置通知始终执行!
继续递归
跳到了前置通知类MethodBeforeAdviceInterceptor的invoke
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
注意这里不一样了!它是先调用自己再调执行链,这也就是为什么前置通知早于方法执行
before方法执行完之后,进proceed了,递归即将结束:
所有的增强器取出来了,并执行了before 这里递归就结束了,调用目标方法:invokeJoinpoint
然后是后置通知各种,有异常就走之前try finally那里。
至此aop具体逻辑结束!
总结下易翻车点:
1.注意看自己的源码是哪个类,不然很懵逼,因为调了很多个类的同名方法invoke。
2.注意看是递归,和递归结束条件
3.注意invoke的实现,对于不同的增强器的逻辑是不一样的
4.增强器那个集合是有顺序好的
Spring-AOP源码分析随手记(二)的更多相关文章
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
- Spring AOP 源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- spring aop 源码分析(二) 代理方法的执行过程分析
在上一篇aop源码分析时,我们已经分析了一个bean被代理的详细过程,参考:https://www.cnblogs.com/yangxiaohui227/p/13266014.html 本次主要是分析 ...
- spring aop 源码分析(三) @Scope注解创建代理对象
一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...
随机推荐
- C#数组2(多维数组)
using System; namespace ConsoleApp3 { struct WuGong { public string Name; public int Attack; } class ...
- PHP通过session判断防止表单重复提交实例
PHP通过session判断防止表单重复提交实例,当用户提交表单后,为防止重复操作,通过session来判断是否为初次提交,否则让他返回到之前表单页面. 当前表单页面is_submit设为0 SESS ...
- 服务端性能测试技能tree
ALL: Left: Right: 摘抄一下(觉得不错) 以下来自百度百科 ---- 软件性能测试 软件性能测试是在交替进行负荷和强迫测试时常用的术语.理想的“软件性能测试”(和其他类型的测试)应在需 ...
- plus.webview.create 使用方法
plus.webview.create( "xxx.html", //打开页面地址 "xxx", //打开页面id值 { //窗口样式 width: '100% ...
- Spinner在Dialog中的使用效果
版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/91 背景: 记得很久以前,碰到一个需求场景,需要在Andr ...
- 移动OA办公——Smobiler第一个开源应用解决方案,快来get吧
产品简介 SmoONE是一款移动OA类的开源解决方案,通过Smobiler平台开发,包含了注册.登陆.用户信息等基本功能.集成了OA中使用场景较多的报销.请假.部门管理.成本中心等核心功能. 免费获取 ...
- java annotation使用介绍
还望支持个人博客站:http://www.enjoytoday.cn 介绍 Annotation的中文名字叫注解,开始与JDK 1.5,为了增强xml元数据和代码的耦合性的产物.注解本身并没有业务逻辑 ...
- BayaiM__MySQL错误对照表
BayaiM__MySQL错误对照表 原创 作者:bayaim 时间:2016-06-16 09:16:29 33 0删除编辑 ------------------------------------ ...
- man -k, man -f : nothing appropriate ; 更新 whatis 数据库
man 有两个选项: -f, –whatis Equivalent to whatis. Display a ) for details. -k, –apropos Equivalent to apr ...
- kafka相关操作
kafka安装 下载 wget http://apache.gree.com/apache/kafka/1.0.2/kafka_2.11-1.0.2.tgz tar -zxvf kafka_2.11- ...