spring——AOP原理及源码(五)
前情回顾:
在上一篇中,通过 wrapIfNecessary 方法,我们获取到了合适的增强器(日志方法)与业务
类进行包装,最终返回了我们业务类的代理对象。

本篇我们将从业务方法的执行开始,看看增强器(日志方法)是怎么在方法执行的前后和发
生异常时被调用的。以及在文章的最后总结整个AOP的执行流程。
1、调试的起点:
给测试方法打上断点,然后一直跳到下一个断点直到执行方法,如下

接着进入方法,会被intercept方法拦截
进入断点:
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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 = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
intercept
intercept 方法从上往下看:
16~18:获取目标类(注意不是代理对象)

19:通过目标类和目标方法获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
2、重点探究获取拦截器链的过程
进入 getInterceptorsAndDynamicInterceptionAdvice

继续进入 this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass)
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
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;
}
getInterceptorsAndDynamicInterceptionAdvice
以上代码从上往下看:
5:创建拦截器链
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length)
12~32:
- 遍历所有增强器
- 经过一系列判断,将增强器放入interceptorList中 :
- interceptorList.addAll(Arrays.asList(interceptors))
46:将拦截器链返回
接下来将拦截器链返回,并存入缓存中

最后将拦截器链返回

这就是拦截器链获取的过程
接下来来到 intercept 方法的真正执行部分:
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
通过new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy) 获取到方法拦截器链
一进来先调用父类方法:

设置好代理对象、目标类、目标方法、拦截器链等一系列属性:

接着一路返回后调用 proceed 方法进行执行:
@Override
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);
}
}
proceed
3~6:可以看到有一个从-1开始的索引,这是用来记录当前执行次数的(这里的size为5对应我们的五个增强器)

8~9:每次从拦截器链中获取一个增强器,索引加一
10:判断这个增强器是不是 InterceptorAndDynamicMethodMatcher 类型,我们这里判断不满足,来到了else,返回调用 invoke 方法的结果

接下来我们进入这个执行过程
invoke方法调用proceed方法

来到proceed方法继续判断索引大小

往下走又来到 invoke 方法,从下图可以看到当前是异常增强器的invoke()

进入invoke
先 return 调用 proceed 方法
可以看到下图中 catch 部分,说明如果有出现异常,会在catch部分调用增强器方法,并抛出异常

接下来又是调用AfterReturning的invoke过程

下图可以看到又调用了proceed

中间的过程也一样,这里就不演示了
最终我们的索引来到末尾
整个过程开始从内到外执行日志方法
开始调用日志方法打印:

抛出异常

最终拦截器链调用完毕,得到结果:

以上可以看到,整个执行流程是一个递归调用的过程,对之前排好序的拦截器链,通过索引判断界限,一层一层往里调用,最终递归回来一层层执行增强器(日志方法)

3、AOP总结:
通过@EnableAspectJAutoProxy 注解,给容器中注册 AnnotationAwareAspectJAutoProxyCreator,这个组件是一个后置处理器
会在每一个bean创建之前执行它的后置处理器方法来获取对应增强器,并获取到目标代理对象
在执行切面方法时,通过代理对象和增强器等信息,获取到拦截器链
拦截器链在包装处理后进入执行流程,嵌套调用后执行增强器方法
aop uml图
以下UML图是我对AOP的理解,如果有不对之处,欢迎大家指出

spring——AOP原理及源码(五)的更多相关文章
- spring——AOP原理及源码(一)
教程共分为五篇,从AOP实例的构建及其重要组件.基本运行流程.容器创建流程.关键方法调用.原理总结归纳等几个方面一步步走进AOP的世界. 本篇主要为读者演示构建AOP实例及AOP核心组件分析. 一.项 ...
- spring——AOP原理及源码(四)
前情回顾: 上文我们一路分析了从容器创建开始直到我们的AOP注解导入的核心组件AnnotationAwareAspectJAutoProxyCreator执行postProcessBeforeInst ...
- spring——AOP原理及源码(二)
回顾: 在上一篇中,我们提到@EnableAspectJAutoProxy注解给容器中加入了一个关键组件internalAutoProxyCreator的BeanDefinition,实际类型为 An ...
- spring——AOP原理及源码(三)
在上一篇中,我们创建并在BeanFactory中注册了AnnotationAwareAspectJAutoProxyCreator组件.本篇我们将要探究,这个组件是在哪里以及何时发挥作用的. 调试的起 ...
- 【Spring】Spring IOC原理及源码解析之scope=request、session
一.容器 1. 容器 抛出一个议点:BeanFactory是IOC容器,而ApplicationContex则是Spring容器. 什么是容器?Collection和Container这两个单词都有存 ...
- Spring AOP介绍及源码分析
转自:http://www.uml.org.cn/j2ee/201301102.asp 软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程:前者是为了提高开发效率,而后者则使用了归纳法,把 ...
- Spring中AOP原理,源码学习笔记
一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...
- spring MVC 原理及源码解析
首先要知道springmvc的核心控制器dispatcherServlet(继承自httpServlet) 一般httpServlet的请求过程: 1.初始化(创建servlet实例时)时会执行ser ...
- 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析
在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...
随机推荐
- CCS|ANSI|中华人民共和国标准化法|国标|ISO|IEC|Ieeexplore|
国家的标准的有效期,标龄是5年.强制性标准是是指为保障人体的健康.人身.财产安全的标准和法律.行政法规定强制执行的标准,如药品标准.食品卫生标准. CCS:分类法简写图 国际标准,比如美国国家标准AN ...
- vue2.0学习之组件间通信
/* child.vue*/ 子组件 <template> <div> /*必须要用div包裹起来,然后在里面写需要的组件内容,这里面和平常写的html是一样的*/ <d ...
- spring-mvc基于注解的配置
将配置文件修改为: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="htt ...
- HotSpot Java对象创建,内存布局以及访问方式
内存中对象的创建.对象的结构以及访问方式. 一.对象的创建 在语言层面上,对象的创建只不过是一个new关键字而已,那么在虚拟机中又是一个怎样的过程呢? (一)判断类是否加载.虚拟机遇到一条new指令的 ...
- 手机遥控Office,变身演讲达人
编者按:在商业演讲中,需要在PPT/Word/Excel文件中切换以达到最佳演讲效果-Office Remote可帮助Windows Phone变身Office的智能遥控.以蓝牙控制电脑,触屏操作多种 ...
- MySQL之数据存储引擎
1.什么是存储引擎: 现实生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处 理文本用txt类型,处理表格用excel,处理图片用png等,数据库中的表也应该有不同的 ...
- mvn相关介绍和命令
1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目完全迁移并应用maven ...
- makefile中的变量赋值
在makefile中赋值方式有:'='.':='.'?='和'+='. A = a $(B) B = b all: echo $(A) #运行结果:echo a b a b 这种赋值方式是没有先后顺序 ...
- Excel中带字母的数字序列自增实现方法
示例: 在A1单元格输入以下公式,然后向下填充公式 =".mr"&ROW()&" {margin-right: "&ROW()& ...
- Mysql错误问题:ERROR 1005 (HY000): Can't create table 'crm_1.tbl_client' (errno: 150)
MySQL外键创建条件: 1.两个表必须是InnoDB数据引擎2.外键表的外键字段必须是主键3.字段类型必须一致 创建表时创建外键: create table tbl_client(userName ...