Spring 源码阅读 70:基于 JDK 的 AOP 代理拦截器链执行(4)- 容易被忽略的 ExposeInvocationInterceptor

简介: 【1月更文挑战第5天】本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。

本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

接上一篇:Spring 源码阅读 69:基于 JDK 的 AOP 代理拦截器链执行(3)- MethodInterceptor 分析

概述

之前的三篇文章,分析了 JdkDynamicAopProxy 的invoke方法,如何获取到拦截器链,并通过 ReflectiveMethodInvocation 的proceed方法执行拦截器链的增强方法和目标方法的逻辑。在拦截器链的增强方法的执行逻辑中,分别分析了五种增强类型对应的拦截器中的invoke方法的逻辑。

但其实,在拦截器链中,还有一个不是这五种增强类型对应的拦截器,它就是 ExposeInvocationInterceptor,这个拦截器并不是与我们配置的增强方法对应的,所以很容易被忽略。

这个拦截器类型,其实我们之前的源码分析中是见过的,我们先回顾一下,它是什么时候被注册的。

ExposeInvocationInterceptor 的注册

Spring AOP 的特性,是通过在后处理器中创建目标 Bean 对象的代理对象来实现的。当 Bean 对象初始化完成之后,会在后处理器中查找与当前 Bean 对象的类型是配的增强逻辑,这个查找的逻辑是由 AbstractAdvisorAutoProxyCreator 的findEligibleAdvisors完成的。

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisorsprotectedList<Advisor>findEligibleAdvisors(Class<?>beanClass, StringbeanName) {
List<Advisor>candidateAdvisors=findCandidateAdvisors();
List<Advisor>eligibleAdvisors=findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors=sortAdvisors(eligibleAdvisors);
   }
returneligibleAdvisors;
}

在这个方法中,前两行代码的作用是找出 Spring 容器中所有的增强以及从这些增强中筛选出与当前 Bean 得类型匹配的增强。之后,通过extendAdvisors方法,对这个增强列表进行了扩展操作。

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#extendAdvisorsprotectedvoidextendAdvisors(List<Advisor>candidateAdvisors) {
}

这个方法在 AbstractAdvisorAutoProxyCreator 中是一个空方法,但是,无论是 XML 配置的切面配置,还是注解配置的切面配置,其对应的后处理器实现类,并不是 AbstractAdvisorAutoProxyCreator 本身,而是它的子类,在子类 AspectJAwareAdvisorAutoProxyCreator 中,重写了这个方法,逻辑如下。

// org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors@OverrideprotectedvoidextendAdvisors(List<Advisor>candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

这里执行了一个方法,我们进入这个方法。

// org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessarypublicstaticbooleanmakeAdvisorChainAspectJCapableIfNecessary(List<Advisor>advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not requiredif (!advisors.isEmpty()) {
booleanfoundAspectJAdvice=false;
for (Advisoradvisor : advisors) {
// Be careful not to get the Advice without a guard, as this might eagerly// instantiate a non-singleton AspectJ aspect...if (isAspectJAdvice(advisor)) {
foundAspectJAdvice=true;
break;
         }
      }
if (foundAspectJAdvice&&!advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
returntrue;
      }
   }
returnfalse;
}

可以看到这个方法的逻辑并不复杂,其主要作用是,如果之前获取到的 Advisor 列表包含符合isAspectJAdvice的判断逻辑(我们配置的增强逻辑都符合这个条件),则向列表的开头增加一个ExposeInvocationInterceptor.ADVISOR

下面我们看一下这里添加的ExposeInvocationInterceptor.ADVISOR

ExposeInvocationInterceptor 是什么

我们直接找到 ExposeInvocationInterceptor 类和ADVISOR常量的定义。

publicfinalclassExposeInvocationInterceptorimplementsMethodInterceptor, PriorityOrdered, Serializable {
/** Singleton instance of this class. */publicstaticfinalExposeInvocationInterceptorINSTANCE=newExposeInvocationInterceptor();
/*** Singleton advisor for this class. Use in preference to INSTANCE when using* Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.*/publicstaticfinalAdvisorADVISOR=newDefaultPointcutAdvisor(INSTANCE) {
@OverridepublicStringtoString() {
returnExposeInvocationInterceptor.class.getName() +".ADVISOR";
      }
   };
// 省略其他代码 }

从以上的代码中可以看出:

  • ExposeInvocationInterceptor 是一个 MethodInterceptor 的实现类,也就是和我们之前分析过的五种增强类型对应的拦截器一样,通过invoke方法来实现其拦截器的逻辑。
  • ADVISOR常量,其实就是一个包含了一个 ExposeInvocationInterceptor 单例对象的 DefaultPointcutAdvisor 对象。

了解了这些,我们可以得出结论,这个拦截器其实和五种增强类型对应的拦截器很相似,因此,要分析它的作用,只要看它的invoke方法就行了。

ExposeInvocationInterceptor 的执行

下面看它的invoke方法源码。

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke@OverridepublicObjectinvoke(MethodInvocationmi) throwsThrowable {
MethodInvocationoldInvocation=invocation.get();
invocation.set(mi);
try {
returnmi.proceed();
   }
finally {
invocation.set(oldInvocation);
   }
}

方法中除了执行mi的proceed方法外,频繁操作了invocation成员变量,我们看一下这个它是什么。

privatestaticfinalThreadLocal<MethodInvocation>invocation=newNamedThreadLocal<>("Current AOP method invocation");

它是一个 ThreadLocal 变量。在invoke方法中,会先获取到invocation中的内容,暂存在oldInvocation变量,然后把mi放到invocation中。当miproceed方法执行完之后,再将invocation中最初保存的内容还原。

因为 ExposeInvocationInterceptor 是拦截器链中第一个拦截器,因此,它的作用是,在进入拦截器执行逻辑的时候,将 MethodInvocation 方法调用对象暴露到 ThreadLocal 中,在拦截器链执行完之后再还原。根据之前的分析,我们也能知道,这里参数传入的值,就是在 JdkDynamicAopProxy 的invoke方法中创建的 ReflectiveMethodInvocation 对象。

这也是 ExposeInvocationInterceptor 过滤器起到的作用。

总结

本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。

目录
相关文章
|
6天前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
16 1
|
6天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
25 6
|
6天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
32 3
|
6天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
4天前
|
前端开发 Java 关系型数据库
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
|
4天前
|
前端开发 Java 编译器
详解Spring与JDK注入
依赖注入是Spring框架的核心概念之一,它通过将对象之间的依赖关系外部化,实现了松耦合和可测试性。面向切面编程则允许开发人员将横切关注点(如日志、事务管理)从应用程序的主要业务逻辑中分离出来,以提高代码的模块化和可维护性。
11 4
|
6天前
|
JSON 前端开发 Java
【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】Spring全家桶实现AOP-统一处理
6 0
|
6天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
12 0
|
6天前
|
Java Spring 容器
Spring AOP浅谈
Spring AOP浅谈
12 1
|
6天前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
23 6
Spring高手之路18——从XML配置角度理解Spring AOP