Spring 源码阅读 72:基于 CGLIB 的 AOP 代理的原理(2)- 拦截器的查找与执行

简介: 【1月更文挑战第7天】本文分析了基于 CGLIB 的 AOP 代理如何查找和执行拦截器链,其主要的逻辑在 DynamicAdvisedInterceptor 的intercept方法执行。


基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 60:通过 JDK 动态代理或者 CGLIB 创建 AOP 代理对象

接上一篇:Spring 源码阅读 71:基于 CGLIB 的 AOP 代理的原理(1)- DynamicAdvisedInterceptor 分析

概述

上一篇分析了基于 CGLIB 的 AOP 代理对象执行 AOP 增强逻辑的入口,也就是 DynamicAdvisedInterceptor 的intercept方法,本文来进入这个方法,分析其执行增强逻辑的原理。

DynamicAdvisedInterceptor 的 intercept 方法

首先看这个方法的源码。

方法虽然很长,但是真正执行逻辑的代码并不多。

ObjectoldProxy=null;
booleansetProxyContext=false;
Objecttarget=null;
TargetSourcetargetSource=this.advised.getTargetSource();

方法的一开始会声明一些变量,这些变量会在后面的流程中看到,其中包括从advised属性中获取到的被代理的目标对象。advised成员变量是在创建 DynamicAdvisedInterceptor 是通过构造方法的参数传入的。

后续的流程会进入一个try语句块。

if (this.advised.exposeProxy) {
// Make invocation available if necessary.oldProxy=AopContext.setCurrentProxy(proxy);
setProxyContext=true;
}

这个if语句块中,会将代理对象放到 AopContext 上下文中,不过默认情况下,this.advised.exposeProxy的值是false,因此这里的逻辑不会被执行。

拦截器的查找

// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...target=targetSource.getTarget();
Class<?>targetClass= (target!=null?target.getClass() : null);
List<Object>chain=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

接下来会进入一个重要的步骤,就是根据目标类型和当前调用的方法,查找拦截器链。如果你读过之前关于基于 JDK 动态代理的 AOP 代理的原理,就会发现这个用来查找拦截器链的方法很熟悉。其实,这个方法跟 JdkDynamicAopProxy 的invoke方法中调用的getInterceptorsAndDynamicInterceptionAdvice方法是同一个方法。

关于这个方法的原理,我之前写过 4 篇文章来详细介绍,可以阅读之前的文章来了解其中的原理。

传送门:如何获取拦截器链:1234

拦截器的执行

// 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=newCglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}

接下来就是对获取到的拦截器链进行判断,如果拦截器链是空的,且方法是public修饰的,则不需要执行拦截器的逻辑。在if语句块中,首先通过 AopProxyUtils 类的adaptArgumentsIfNecessary方法,对调用方法的参数进行处理,这里主要是在有必要的情况下,对方法参数列表中的可变参数进行类型适配。然后,通过methodProxyinvoke方法直接调用目标方法。

如果拦截器链不为空,那么则会通过构造方法创建一个 CglibMethodInvocation 对象,然后调用它的proceed方法,执行拦截器链的增强逻辑。

我们先看一下 CglibMethodInvocation 类。

可以看到,它是 ReflectiveMethodInvocation 的子类,而 ReflectiveMethodInvocation 是基于 JDK 动态代理的 AOP 代理回调方法中用来执行拦截器链逻辑的类型,因此可以推测,这两种代理方式的拦截器链执行逻辑应该也大同小异。

接下来看一下 CglibMethodInvocation 的类定义和构造方法。

privatestaticclassCglibMethodInvocationextendsReflectiveMethodInvocation {
@NullableprivatefinalMethodProxymethodProxy;
publicCglibMethodInvocation(Objectproxy, @NullableObjecttarget, Methodmethod,
Object[] arguments, @NullableClass<?>targetClass,
List<Object>interceptorsAndDynamicMethodMatchers, MethodProxymethodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Objectthis.methodProxy= (Modifier.isPublic(method.getModifiers()) &&method.getDeclaringClass() !=Object.class&&!AopUtils.isEqualsMethod(method) &&!AopUtils.isHashCodeMethod(method) &&!AopUtils.isToStringMethod(method) ?methodProxy : null);
   }
// 省略其他代码}

它是 CglibAopProxy 的一个静态内部类,在构造方法中,调用了父类的构造方法,也就是 ReflectiveMethodInvocation 的构造方法,此外,还初始化了methodProxy变量。methodProxy初始化的逻辑也比较简单,主要是确保被代理的方法是public修饰的不是声明在 Object 中的方法,且不是equalshashCodetoString这三个方法。

对父类构造方法的调用主要是一些属性的初始化,之前介绍 ReflectiveMethodInvocation 的时候,已经介绍过,这里不再赘述。

了解完构造方法之后,再看其执行拦截器链逻辑的proceed方法。

// org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed@Override@NullablepublicObjectproceed() throwsThrowable {
try {
returnsuper.proceed();
   }
catch (RuntimeExceptionex) {
throwex;
   }
catch (Exceptionex) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
throwex;
      }
else {
thrownewUndeclaredThrowableException(ex);
      }
   }
}

其核心逻辑只有一行,就是调用父类的proceed方法,也就是 ReflectiveMethodInvocation 的proceed方法,到这里,接下来的逻辑就跟基于 JDK 动态代理的 AOP 代理回调执行拦截器链的逻辑相同了,在之前的文章中,也详细分析过这个方法的执行逻辑,你可以通过之前的文章来了解其中的细节。

传送门:ReflectiveMethodInvocation 分析MethodInterceptor 分析

总结

本文分析了基于 CGLIB 的 AOP 代理如何查找和执行拦截器链,其主要的逻辑在 DynamicAdvisedInterceptor 的intercept方法执行。对于拦截器链的查找和执行逻辑,与基于 JDK 动态代理的 AOP 代理的执行逻辑共用了大部分的代码。

目录
相关文章
|
16小时前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
11 6
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
2天前
|
监控 安全 Java
Spring cloud原理详解
Spring cloud原理详解
13 0
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
3月前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
26 0
|
4月前
AOP&面向切面编程
AOP&面向切面编程
56 0
|
4月前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
|
4月前
|
数据库
AOP(面向切面编程)的基本概念和原理
AOP(面向切面编程)的基本概念和原理
91 0
|
6月前
|
缓存 监控 Java
Spring框架之AOP(面向切面编程)
Spring框架之AOP(面向切面编程)
34 0