写在前面

  时间断断续续,这次写一点关于spring aop拦截器链的记载。至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动态代理

获取拦截器链

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
       //获取拦截器链  
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
if (chain.isEmpty()) {
// 如果拦截器链为空,则执行目标方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// 封装拦截器链的执行方法
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 拦截器链执行
retVal = invocation.proceed();
}
     ...
}

ReflectiveMethodInvocation 的结构

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
protected final Object proxy;
protected final Object target;
protected final Method method;
protected Object[] arguments;
private final Class<?> targetClass;
private Map<String, Object> userAttributes;
//拦截器链
protected final List<?> interceptorsAndDynamicMethodMatchers;
   //起始基数 默认为-1
private int currentInterceptorIndex = -1;

   @Override//拦截器执行入口
public Object proceed() throws Throwable {
// 如果自增系数和拦截器链中拦截器数量相同(则代表,拦截器依次执行完毕)
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
       //拦截器执行完毕,执行目标方法;
return invokeJoinpoint();
}
     //根据起始基数,依次获对应的拦截器。(每次获取拦截器,起始基数都自增一次)
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
     //这里获取的具体拦截器只会是两种类型(InterceptorAndDynamicMethodMathcher或者MethodInterceptor)      InterceptorAndDynamicMethodMathcher:拦截器的动态方法匹配(这个用的比较少)
MethodInterceptor :方法拦截器(我们的aop拦截基本上都是methodInterceptor)
     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// 具体的拦截器执行执行自己的invoke方法,将拦截器链传到里面去了。类似一个链,做递归调用,最有一个拦截器执行完毕(自增系数会和拦截器数量相同,执行目标方法),最后每一个拦截器依次返回,拦截器链执行完毕
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
实现类 ReflectiveMethodInvocation 实现接口 ProxyMethodInvocation 继承接口 MethodInvocation 继承接口 Invocation 继承接口 Joinpoint
接口 MethodInterceptor 继承接口 Interceptor 继承接口 Advice

可以看看 MethodInterceptor 自己的抽象方法
public interface MethodInterceptor extends Interceptor {
//拦截器就是通过这个方法的实现,一次执行拦截器链,直到拦截器链中的拦截器执行完毕
Object invoke(MethodInvocation invocation) throws Throwable;
}

看一个 MethodInterceptor 的具体实现 : TransactionInterceptor(事务通知)

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

    @Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//先准备事物的环境,执行事物执行的相关操作,具体见之前的博客 (spring---transaction(1)---源代码分析(事务的拦截器TransactionInterceptor))
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
          //准备好环境之后,回调拦截器链,拦截器链中的起始基数自增,执行下一个拦截器的invoke方法。如果这是最后一个拦截器,那么拦截器链中的起始基数和拦截器数相同,执行目标方法
return invocation.proceed();
}
});
}
}

再看一个 MethodInterceptor 的实现:MethodBeforeAdviceInterceptor(前置通知)

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
   private MethodBeforeAdvice advice;
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
     //执行前置通知的具体方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
     //执行完成之后,回调拦截器链,执行下一个拦截器的invoke方法
return mi.proceed();
}
}

再看一个 MethodInterceptor 的实现:AspectJAfterAdvice(后置通知)

public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {

    @Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
        //因为是后置通知,直接调用拦截器链的下一个拦截器。如果拦截器全部调用完毕,会执行目标方法
return mi.proceed();
}
finally {
       //后置通知具体方法执行。到这里,所有的拦截器以及全部执行完毕,且目标方法已经执行。所有在这里执行后置拦截器的后置方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}

再看一个 MethodInterceptor 的实现:AspectJAfterThrowingAdvice(环绕通知)

public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {

    @Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
       //因为是环绕通知,直接调用拦截器链的下一个拦截器。将目标方法的执行方法try cathch 代码块中
return mi.proceed();
}
catch (Throwable t) {
        //判断是否是目标方法的异常信息
if (shouldInvokeOnThrowing(t)) {
          //如果目标方法在执行的过程中 抛出异常,则执行环绕通知的异常方法。  
invokeAdviceMethod(getJoinPointMatch(), null, t);
}
        //继续抛出异常,不影响正常的业务逻辑
throw t;
}
}
}

一张图清晰解释拦截器原则

总结一下:

  spring的aop设计,并不是所有的通知对象一产生出来就是拦截器,spring是将所有的通知转化为了拦截器(MethodInterceptor 的子类)。中间有经过转换,到了拦截器链中,作为拦截器统一处理。这次先不写转换为拦截器的过程

spring---aop(3)---Spring AOP的拦截器链的更多相关文章

  1. Spring异步调用原理及SpringAop拦截器链原理

    一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTI ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. 2017.3.31 spring mvc教程(三)拦截器

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  4. Spring Boot项目中如何定制拦截器

    本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...

  5. 33、[源码]-AOP原理-获取拦截器链-MethodInterceptor

    33.[源码]-AOP原理-获取拦截器链-MethodInterceptor

  6. java 动态代理—— Mybaties 拦截器链基本原理实现

    1.摘要 Mybaties 中有个分页插件,之前有特意的去了解了一下原理 :https://www.cnblogs.com/jonrain0625/p/11168247.html,从了解中得知分页插件 ...

  7. JdkDynamicAopProxy 拦截器链的获得与递归执行

    JdkDynamicAopProxy类的invoke方法 1.获得拦截器链 List<Object> chain = this.advised.getInterceptorsAndDyna ...

  8. SpringBoot系列(十一)拦截器与拦截器链的配置与使用详解,你知道多少?

    往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件详解 SpringBoot系列(四)web静 ...

  9. 我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器

    回到目录 AOP面向切面的编程,也称面向方面的编程,我更青睐于前面的叫法,将一个大系统切成多个独立的部分,而这个独立的部分又可以方便的插拔在其它领域的系统之中,这种编程的方式我们叫它面向切面,而这些独 ...

随机推荐

  1. React-Native 之 环境配置和简单使用

    # 前言 学习本系列内容需要具备一定 HTML 开发基础,没有基础的朋友可以先转至 HTML快速入门(一) 学习 本人接触 React Native 时间并不是特别长,所以对其中的内容和性质了解可能会 ...

  2. 02 Go 1.2 Release Notes

    Go 1.2 Release Notes Introduction to Go 1.2 Changes to the language Use of nil Three-index slices Ch ...

  3. python基础--random模块

    python使用random生成随机数 下面是主要函数random.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0random.randint(a, b)生成的 ...

  4. supervisor的安装和配置

    1. 安装 yum install supervisor 2.配置 [unix_http_server] file=/tmp/supervisor.sock ;UNIX socket 文件,super ...

  5. luoguP2735 电网 Electric Fences

    一道校内模拟赛遇见的题 ** 不会正解就真的很麻烦的 数学题 ** 有一种东西叫 皮克定理 发现的千古神犇: 姓名:George Alexander Pick(所以叫皮克定理呀 国籍:奥地利(蛤!竟然 ...

  6. Linux学习笔记:mkdir创建文件夹

    文件夹,即目录,在linux中使用mkdir创建. 语法:mkdir dir_name 通过 mkdir 命令可以实现在指定位置创建以 dir_name(指定的文件名)命名的文件夹或目录.要创建文件夹 ...

  7. 超简单的java爬虫

    最简单的爬虫,不需要设定代理服务器,不需要设定cookie,不需要http连接池,使用httpget方法,只是为了获取html代码... 好吧,满足这个要求的爬虫应该是最基本的爬虫了.当然这也是做复杂 ...

  8. 洛谷 P1992 不想兜圈的老爷爷 题解

    洛谷 P1992 不想兜圈的老爷爷 题解 题目描述 一位年过古稀的老爷爷在乡间行走 而他不想兜圈子 因为那会使他昏沉 偶然路过小A发扬助人为乐优良传统 带上地图 想知道路况是否一定使他清醒 usqwe ...

  9. 【LOJ】#2067. 「SDOI2016」硬币游戏

    题解 c一样的就是一个独立的游戏 我们对于2和3的指数 sg[i][j] 表示\(c \cdot 2^i \cdot 3^j\)的棋子,只有这个硬币是反面,翻转的硬币是正面的sg值 枚举sg函数所有可 ...

  10. 如何解决谷歌Chrome浏览器空白页的问题

    如何解决谷歌Chrome浏览器空白页的问题   谷歌Chrome浏览器突然不打开任何网页,无论是任何站点(如http://www.baidu.com), 还是Chrome浏览器的设置页面(chrome ...