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

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自定义自动代理创建逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【FAQ】API6低代码开发问题汇总

参考文档&#xff1a; 低代码开发参考文档&#xff1a; 文档中心:使用低代码进行开发 基于景区模板开发元服务&#xff1a; 文档中心:模板简介 使用API6低代码开发遇到的问题汇总情况如下&#xff1a; 1、低代码环境下&#xff0c;如何实现box-shadow阴影效果的配置&#…

K8s核心概念 Controller

K8s核心概念 Controller Kubernetes核心概念 Controller一、pod控制器controller1.1 Controller作用及分类1.2 Deployment1.2.1 Replicaset控制器的功能1.2.2 Deployment控制器的功能1.2.3 Deployment用于部署无状态应用1.2.4 创建deployment类型应用1.2.5 访问deployment1.2.6…

优思学院|六西格玛管理:依据事实的质量管理方式

一个企业的质量管理制度是否规范&#xff0c;也就是质量管理体系是否很完备的问题&#xff0c;要考察管理体系是否还有哪里不尽完美&#xff1f;各部门之间的连系、调整是否能够顺利进行&#xff1f;各自是否达成在质量保证上的任务等&#xff0c;进行质量管理体系的审核&#…

通用文字识别OCR 之实现自动化办公

摘要 随着技术的发展&#xff0c;通用文字识别&#xff08;OCR&#xff09;已经成为现代办公环境中不可或缺的工具之一。OCR技术可以将印刷或手写文本转换为可编辑或可搜索的数字文本&#xff0c;极大地提高了办公效率并实现了自动化办公。本文将深入探讨OCR技术在实现自动化办…

关于你欠缺的NoSQL中的redis和mongoDB

文章目录 前言一、在string list hash结构中&#xff0c;每个至少完成5个命令&#xff0c;包含插入 修改 删除 查询&#xff0c;list 和hash还需要增加遍历的操作命令1、STRING类型2、List类型数据的命令操作&#xff1a;3、举例说明list和hash的应用场景&#xff0c;每个至少一…

经济和行政手段使双高企业降低能耗总量和能耗强度,提高能源利用效率-安科瑞黄安南

摘要 2022年6月29日工信部、发改委、财政部、生态环境部、国资委、市场监管总局六部门联合下发《关于印发工业能效提升行动计划的通知》&#xff08;工信部联节〔2022〕76号&#xff0c;以下简称《行动计划》&#xff09;&#xff0c;主要目的是为了提高工业领域能源利用效率&…

黄皮书-线接触热弹流润滑 Fortran+Matlab转译代码

原Fortran代码有错误&#xff0c;进行了修改&#xff0c;数值上差别不大。根据Fortran代码转的Matlab&#xff0c;可以完美运行&#xff0c;但是因为精度问题有差异&#xff0c;只能说趋势是一致的。 需要私我-资源里只是Fortran运行结果

Rdkit|分子3D构象生成与优化

github; 地址 文章目录 Rdkit|分子3D构象生成与优化构象生成算法概述基于距离&#xff08;distance-based&#xff09;代码示例 距离几何算法生成3D结构距离几何ETKDG生成3D构象距离几何ETKDG生成多构象将Conformer类转为Mol类手动对齐 距离几何ETKDGMMFF生成3D构象距离几何ETK…

4.日志分布式-ELK

文章目录 日志分布式-ELK概念可以添加的其它组件filebeat 结合 logstash 带来好处为什么要使用 ELK缓存和Fluentd完整日志系统基本特征ELK 的工作原理 部署Elasticsearchjdk环境和防火墙配置安装Elasticsearch修改配置文件优化内存参数启动程序并测试效果安装 Elasticsearch-he…

ThunderScope开源示波器

简介 4CH&#xff0c;1GSa/S 开源示波器。前端很简洁&#xff0c;BUF802LMH6518&#xff0c;ADC是HMCAD1511&#xff0c;用Xilinx A7 FPGA进行控制&#xff0c;数据通过PCIE总线传输到上位机处理。目前这个项目已经被挂到了Xilinx官网&#xff0c;强。 设计日志&#xff1a;h…

AR气象博物馆模拟体验提升青少年认知

国际气象节主要目的是唤起人们对气象工作的重视和热爱。近年来&#xff0c;极端天气频发&#xff0c;人们需要提高警惕&#xff0c;AR气象远程普利用ar技术特有的沉浸式的体感互动&#xff0c;通过模拟演练提升体验者的安全防范意识和求生技巧。 系统结合VR虚拟现实、AR增强现实…

VSCode下载安装(保姆级--一步到胃)

前言 Visual Studio Code&#xff08;简称“VSCode” &#xff09;是Microsoft在2015年4月30日Build开发者大会上正式宣布一个运行于 Mac OS X、Windows和 Linux 之上的&#xff0c;针对于编写现代Web和云应用的跨平台源代码编辑器&#xff0c;可在桌面上运行&#xff0c;并且…

机械臂的雅克比矩阵推导

1. 线速度和角速度的递推通式推导 p i p i − 1 R i − 1 r i − 1 , i i − 1 \mathbf{p}_{i}\mathbf{p}_{i-1}\mathbf{R}_{i-1} \mathbf{r}_{i-1, i}^{i-1} pi​pi−1​Ri−1​ri−1,ii−1​ p i − 1 \mathbf{p}_{i-1} pi−1​是 { i − 1 } \{i-1\} {i−1}坐标系的原点的…

[PHP]解决exec执行unzip出现中文文件名乱码的问题

查看Linux编码&#xff0c;如下图可看出Linux编码是zh_CN.UTF-8 问题截图&#xff1a; 以下代码都会产生乱码 exex(unzip -d /xxx /x/test.zip); exex(unzip -O zh_CN.UTF-8 -d /xxx /x/test.zip); exex(unzip -I zh_CN.UTF-8 -d /xxx /x/test.zip); 解决方法&#xff1a; e…

大模型开发(五):实现Jupyter本地调用OpenAI API

全文共3000余字&#xff0c;预计阅读时间约15分钟 | 满满干货&#xff0c;建议收藏&#xff01; 大模型开发(五)&#xff1a;实现Jupyter本地调用OpenAI API OpenAI作为本轮大语言模型技术进步的先驱&#xff0c;其系列大型模型在效果上一直保持着领先。其推出的各类模型如文本…

Ubuntu搭建docker+laradock

使用Ubuntu搭建dockerlaradock windows 下载Ubuntu工具二选一 链接&#xff1a;https://pan.baidu.com/s/154K6MKdFZxWqaTn2q-6MSQ 提取码&#xff1a;06lc https://www.jianshu.com/p/b7e11d0dbe8c借鉴地址&#xff1a;https://zhuanlan.zhihu.com/p/547169542 备注&#x…

JS-27 前端数据请求方式;HTTP协议的解析;JavaScript XHR、Fetch的数据请求与响应函数;前端文件上传XHR、Fetch;安装浏览器插件FeHelper

目录 1_前端数据请求方式1.1_前后端分离的优势1.2_网页的渲染过程 – 服务器端渲染1.3_网页的渲染过程 – 前后端分离 2_HTTP协议的解析2.1_HTTP概念2.2_网页中资源的获取2.3_HTTP的组成2.4_HTTP的版本2.5_HTTP的请求方式2.6_HTTP Request Header2.7_HTTP Response响应状态码 3…

成为机器人工程师需要学习那些技术

机器人工程师是未来比较吃香的工作岗位&#xff0c;要成为机器人工程师&#xff0c;ChatGPT的回答是&#xff0c;建议你需要学习以下技术&#xff1a; 1、机械工程&#xff1a;了解机械结构、运动学和动力学&#xff0c;以及机械设计和制造方面的知识。 2、电子工程&#xff1…

opencv -11 图像运算之按位逻辑运算(图像融合图像修复和去除)

按位逻辑运算是一种对图像进行像素级别的逻辑操作的方法&#xff0c;使用OpenCV的按位逻辑运算函数可以对图像进行位与&#xff08;AND&#xff09;、位或&#xff08;OR&#xff09;、位非&#xff08;NOT&#xff09;和位异或&#xff08;XOR&#xff09;等操作。 通俗点就是…

i.MX6ULL(十六) linux 设备驱动

一 简介 Linux设备驱动是指驱动Linux内核与硬件设备进行通信的软件模块。设备驱动通常分为两类&#xff1a;字符设备驱动和块设备驱动。 设备驱动的主要功能包括&#xff1a; 设备初始化&#xff1a;在系统启动时&#xff0c;设备驱动需要初始化相应的硬件设备&#xff0c;设…