spring5源码篇(10)——spring-aop代理过程

news2024/11/20 7:23:20

spring-framework 版本:v5.3.19

文章目录

  • 1、ProxyFactory
    • 1.1、createAopProxy() 创建AopProxy
    • 1.2、getProxy() 创建代理对象
    • 1.3、JdkDynamicAopProxy#invoke 代理逻辑
      • 1.3.1、advised.getInterceptorsAndDynamicInterceptionAdvice() 匹配添加的advisor并转化成所需对象
      • 1.3.2、invocation.proceed() 执行代理逻辑
  • 2、DefaultAdvisorAutoProxyCreator
  • 3、AnnotationAwareAspectJAutoProxyCreator

废话少说,上代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在spring-aop中,无论是xml还是注解还是硬编码配置的代理,最后都会通过proxyFactory去实现真正的代理。
proxyFactory的使用也很简单,给他一个target和一个advisor即可生成代理类。

target就是被代理对象,而 advisor 可以简单的理解成 advice + pointcut

1、ProxyFactory

所以proxyFctory是如何生成代理对象的?

追踪ProxyFactory.getProxy()方法
在这里插入图片描述

1.1、createAopProxy() 创建AopProxy

这里可能创建 CglibAopProxy 也可能创建 JdkDynamicAopProxy,分别对应两种不同的动态代理方式

AopProxy 由 AopProxyFactory生成,而spring默认的AopProxyFactory只有 DefaultAopProxyFactory 这一个实现。
在这里插入图片描述
通俗的说就是:如果目标对象至少实现了一个接口则使用jdk,反之使用cglib。同时也可配置 optimize = true 或者 proxyTargetClass = true 强制使用cglib代理。

1.2、getProxy() 创建代理对象

这里就是用cglib或者是jdk的方式去创建一个代理对象了。以jdk为例,其实就是我们平时使用jdk动态代理的代码。
JdkDynamicAopProxy.getProxy()
在这里插入图片描述
注意看 Proxy.newProxyInstance 的 InvocationHandler 传了this,说明 JdkDynamicAopProxy 同时也实现了 InvocationHandler 。
也就是最最最重要的代理逻辑就在 JdkDynamicAopProxy.invoke 方法。

1.3、JdkDynamicAopProxy#invoke 代理逻辑

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			//特殊方法以及对代理对象的配置信息进行操作,如不代理equals,hashCode
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			//如果配置 exposeProxy = true 则把当前代理对象放到线程ThreadLocalMap中
			// 当想要自己调自己同时代理不失效的时候可以配为true
			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			//匹配添加的advisor
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			if (chain.isEmpty()) {
				//如果为空直接反射调用
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				//不为空则进行代理
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}

			//对返回值的检查及特殊情况的处理
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				//特殊情况:当返回自身时,则返回代理后的对象
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

1.3.1、advised.getInterceptorsAndDynamicInterceptionAdvice() 匹配添加的advisor并转化成所需对象

在这里插入图片描述

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		//分 PointcutAdvisor、IntroductionAdvisor、其他 三个分支
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				//PointcutAdvisor分支
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				//匹配 prefiltered 和 class
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					//匹配 method
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						//如果MethodMatcher是IntroductionAwareMethodMatcher的话,则用IntroductionAwareMethodMatcher.matches
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						//反之直接用MethodMatcher.matches
						match = mm.matches(method, actualClass);
					}
					if (match) {
						//如果匹配则将 advisor 转化成 MethodInterceptor[]
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// 如果runtime为true,说明需要根据参数动态匹配,添加 InterceptorAndDynamicMethodMatcher
							for (MethodInterceptor interceptor : interceptors) {
								//InterceptorAndDynamicMethodMatcher = MethodInterceptor + MethodMatcher
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							// 反之直接加到list
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				//IntroductionAdvisor分支
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				//匹配 prefiltered 和 class
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					// 如果匹配 将advisor转成Interceptor[] 放到list
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				//其他类型的Advisor分支
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

至于是如何转化的,用了适配器模式。遍历所有 AdvisorAdapter,如果这个适配器可以转化,则进行转化。注意看,这里就算适配了也没有退出循环,也就是说一个advisor可能同时被多个适配器适配,这也就是为什么会返回数组的原因。
register.getInterceptors(advisor)
在这里插入图片描述
与其说转化advisor,我个人更喜欢理解成转化advice。
内置的适配器跟内置的advice是对应上的
在这里插入图片描述
以before为例
在这里插入图片描述
不过为什么没有环绕通知的适配器,因为我们用的AspectJAroundAdvice环绕通知本来就是一个MethodInterceptor
在这里插入图片描述

总之这一步会匹配符合的advisor并将其转化成所需对象(如MethodInterceptor)。匹配的逻辑根据advisor的类型分为三个分支:PointcutAdvisor、IntroductionAdvisor、其他,简单的说,他们的区别就是分别为匹配类和方法,匹配类,无需匹配直接添加。但无论是哪种分支,最终都会将advisor(直接或通过适配器)转化成 MethodInterceptor 或 InterceptorAndDynamicMethodMatcher。

1.3.2、invocation.proceed() 执行代理逻辑

在这里插入图片描述
为空的情况没什么好说的直接反射调用就是了。
不为空的情况下,会将所有代理所需要的变量封装成一个 MethodInvocation ,并递归调用proceed()执行代理逻辑。
invocation.proceed()
在这里插入图片描述
以beforeAdvice转化的MethodBeforeAdviceInterceptor为例看一下是如何递归的。
在这里插入图片描述
至此,proxyFactory的jdk代理就看完了,cglib的代理也是类似的。无非就是生成代理对象时所用的技术不同而已,像什么advisor,AdvisorAdapter,methodInterceptor,InterceptorAndDynamicMethodMatcher,methodInvocation,等都是一样的。

总结上面的内容粗略画了一个流程图:
在这里插入图片描述

2、DefaultAdvisorAutoProxyCreator

不过我们平时使用spring-aop的时候貌似没有用到proxyFactory。而是只需配置advisor bean或者使用aspectj注解配置切面就可以了。
比如这样
在这里插入图片描述
为什么这里容器得到了bean会是一个代理类呢?什么时候给他代理了?
答案就在import的 DefaultAdvisorAutoProxyCreator
在这里插入图片描述
DefaultAdvisorAutoProxyCreator 本质上是一个SmartInstantiationAwareBeanPostProcessor,即BeanPostProcessor。在容器启动的时候会增强bean,所谓的增强其实就是自动扫描容器中匹配的advisor,并代理目标bean。

postProcessAfterInitialization 方法的增强逻辑在其抽象父类AbstractAutoProxyCreator
在这里插入图片描述

3、AnnotationAwareAspectJAutoProxyCreator

在这里插入图片描述
而 AnnotationAwareAspectJAutoProxyCreator 与 DefaultAdvisorAutoProxyCreator 最大的区别就在于寻找容器所有的advisor时,所使用的方法不同。 AnnotationAwareAspectJAutoProxyCreator 重写了findCandidateAdvisors() 使其支持将 aspectj 注解的内容转化成 advisor。
在这里插入图片描述
所以如果我们只是import了 DefaultAdvisorAutoProxyCreator 是不会去解析 aspectJ 对应的aop注解的。
只有当import了 AnnotationAwareAspectJAutoProxyCreator 才会去解析。这也正是 @EnableAspectJAutoProxy 注解所做的事情。
在这里插入图片描述

由代码也可以看出,当使用了 AnnotationAwareAspectJAutoProxyCreator 就没必要再使用 DefaultAdvisorAutoProxyCreator 了,因为前者不仅会解析 aspectJ 的注解生成advisor,同时也会像后者一样扫描当前已有的 advisor。

除了上面两种AutoProxyCreator,spring中还有 BeanNameAutoProxyCreator(根据bean name去自动创建),AspectJAwareAdvisorAutoProxyCreator(xml配置的aspectJ切面),InfrastructureAdvisorAutoProxyCreator(Infrastructure相关,如事务管理)。当然也可以自己去继承AbstractAdvisorAutoProxyCreator自定义自动代理创建逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/755778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

集群基础3——haproxy负载均衡apache

文章目录 一、环境说明二、安装配置httpd三、安装配置haproxy四、验证http负载均衡五、配置https负载均衡六、haproxy网页监控6.1 监控参数详解6.2 页面操作 一、环境说明 使用haproxy对apache进行负载均衡。 主机IP角色安装服务真实IP&#xff1a;192.168.161.129VIP&#xff…

通识强化学习,初步了解强化学习的运行规则和估值方法

1.强化学习的发展及应用现状 1.1.强化学习的由来 目前&#xff0c;大家认为强化学习&#xff08;Reinforcement Learning, RL&#xff09;的来源与两个领域密切相关&#xff1a;即心理学的动物试错学习和最优控制的优化理论。 这里都是有相应的共性的&#xff0c;在environme…

PostgreSQL MVCC的弊端优化方案

我们之前的博客文章“我们最讨厌的 PostgreSQL 部分”讨论了大家最喜欢的 DBMS 多版本并发控制 (MVCC) 实现所带来的问题。其中包括版本复制、表膨胀、索引维护和真空管理。本文将探讨针对每个问题优化 PostgreSQL 的方法。 尽管 PostgreSQL 的 MVCC 实现是 Oracle 和 MySQL 等…

java每日一题:HashMap的工作原理

面试官&#xff1a;欢迎参加我们的面试。请你解释一下Java中HashMap的工作原理。&#x1f60a; 面试者&#xff1a;HashMap是一种基于哈希表的数据结构&#xff0c;它可以存储键值对。在HashMap内部&#xff0c;使用一个数组来存储数据&#xff0c;数组中的每个位置被称为桶&a…

生信分析案例 Python简明教程 | 视频11

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

C语言之每日一题——杨氏矩阵

今天分享的是杨氏矩阵&#xff0c;题目不是特别难&#xff0c;但是是一道比较考验你对杨氏矩阵的理解&#xff0c;要是你不知道杨氏矩阵的话&#xff0c;那你这道题目就无从下手 杨氏矩阵我们可以这样理解&#xff0c;首先矩阵二字证明他是一个长方形型或者正方形的数组&#x…

【HarmonyOS】元服务隐私协议开发指导样例

【关键字】 隐私、弹窗、元服务、协议 【介绍】 每个元服务必须提供隐私声明&#xff0c;否则将导致提交元服务发布上架时&#xff0c;审核无法通过。隐私声明的具体要求请参见隐私声明规范。用户使用元服务前&#xff0c;必须引导其了解隐私声明信息&#xff0c;获取用户授权…

3.SpringBoot 返回Html界面

1.添加依赖spring-boot-starter-web <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>2.创建Html界面 在Resources/static 文件夹下面建立对应的html&#xff0c…

【亮点回顾】第四届国有企业数智化采购与智慧供应链论坛

7月12日&#xff0c;由中国物流与采购联合会主办、北京筑龙承办的“第四届国有企业数智化采购与智慧供应链论坛”在北京市盛大举行。本届论坛以“数智赋能创新发展”为主题&#xff0c;立足于国有企业采购领域发展前沿&#xff0c;深度聚焦国有企业如何在数字经济发展中发挥引领…

【uView 1.x】中国省市县/区 地区选择器picker【亲测可用】

如果你还没安装uView&#xff0c;请先安装uView 注意&#xff1a;这是uView1.x Picker选择器的用法&#xff0c;uView2.x Picker选择器中没有mode属性 效果图&#xff1a; 把u-picker的mode设置为region地区模式&#xff0c;然后展示在u-input中。 由于uview中自带城市数据包…

echart折线图背景颜色自定义,实心圆点,虚线网格等功能

需求&#xff1a;根据传入的值对背景进行分层颜色展示&#xff0c;比如y轴20-40区间颜色为蓝色&#xff0c;40-50为红色这种&#xff0c;折线图的小圆点设置为实现&#xff0c;实现缩放功能 1.效果如下 2.代码讲解如下 首先下载echarts npm install echarts4.9.0 -S 我这边…

LeetCode·每日一题·931. 下降路径最小和·记忆化搜索

作者&#xff1a;小迅 链接&#xff1a;https://leetcode.cn/problems/minimum-falling-path-sum/solutions/2341965/ji-yi-hua-sou-suo-zhu-shi-chao-ji-xiang-3n58v/ 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 著作权归作者所有。商业转载请联系作者获得授权&am…

Mysql单表多表查询练习

题目要求&#xff1a; 1.查询student表的所有记录 2.查询student表的第2到4条记录 3.从student表查询所有的学生的学号&#xff08;id&#xff09;&#xff0c;姓名&#xff08;name&#xff09;&#xff0c;和院系&#xff08;department&#xff09;的信息 4.从student表…

LeetCode 75 第一题(1768)交替合并字符串

题目: 示例: 分析: 这是LeetCode75 的第一道题目,是一道简单题,题目没那么复杂,就是给两个字符串,要这两个字符串你出一个字符我出一个字符来拼凑出一个新的字符串,如果其中一个字符串用完了则剩下部分全部由另一个字符串出. 我们可以使用两个指针分别指向word1和word2: str…

【hadoop】部署hadoop全分布模式

hadoop全分布模式 全分布模式特点部署全分布模式准备工作正式配置hadoop-env.shhdfs-site.xmlcore-site.xmlmapred-site.xmlyarn-site.xmlslaves对NameNode进行格式化复制到另外两台虚拟机启动 对部署是否成功进行测试 全分布模式特点 真正的分布式环境&#xff0c;用于生产具…

【动手学习深度学习--逐行代码解析合集】19含并行连结的网络(GoogleNet)

【动手学习深度学习】逐行代码解析合集 19含并行连结的网络&#xff08;GoogleNet&#xff09; 视频链接&#xff1a;动手学习深度学习–含并行连结的网络&#xff08;GoogleNet&#xff09; 课程主页&#xff1a;https://courses.d2l.ai/zh-v2/ 教材&#xff1a;https://zh-v…

H3C-Cloud Lab实验-三层交换机实验

实验拓扑图&#xff1a; 实验需求&#xff1a; 1. 按照图示为 PC2 和 PC3 配置 IP 地址和网关 2. PC2 属于 Vlan10&#xff0c;PC3 属于 Vlan20&#xff0c;在三层交换机上配置 Vlanif 三层接口实现 Vlan10 和 Vlan20 三层互通 3. PC2 和 PC3 可以互通 实验步骤&#xff1a…

WAIC2023| AIGC究竟在向善还是向恶而行?

目录 一、常见图像篡改技术二、传统篡改图像检测方法2.1、基于光源和噪声的拼接图像篡改检测方法2.2、基于马尔科夫特征的检测方法 三、基于深度学习的图像篡改检测方法3.1、基于Fisher编码和SVM模型的方法3.2、 基于局部异常特征检测的Mantra-Net方法3.2、基于HRNet的编码器—…

Flink是什么

先看下大数据的发展历程 随着公司业务的增加&#xff0c;各种场景都要大量的业务数据产生&#xff0c;对于这些不断产生的数据如何进行有效的处理&#xff1f; 由此诞生了大数据处理工具&#xff1a; 数据存在关系型数据库&#xff0c;比如mysql&#xff0c;如何分析数据&#…

css之混合模式、文字智能适配背景、文字镂空效果、差值模式、滤色模式、difference、screen、overlay、mix、blend、mode

文章目录 文字智能适配背景(差值模式)文字镂空效果(滤色模式)文字与背景叠加(叠加模式)css3混合模式mix-blend-mode功能表格混合模式的分类 文字智能适配背景(差值模式) <div class"main"><span>文字智能适配背景</span> </div>.main {widt…