spring aop 初探

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

分析JDK动态代理

生成的代理对象  构造函数,入参为 InvocationHandler

public com.sun.proxy.$Proxy164(java.lang.reflect.InvocationHandler)

生成动态代理Class对象:

Class<?> cl = getProxyClass0(loader, intfs);

实例化动态代理对象:

final Constructor<?> cons = cl.getConstructor({ InvocationHandler.class });

cons.newInstance(new Object[]{h});

方法

equals,toString,hashCode 

0 = {Method@10598} "public final boolean com.sun.proxy.$Proxy164.equals(java.lang.Object)"
1 = {Method@10599} "public final java.lang.String com.sun.proxy.$Proxy164.toString()"
2 = {Method@10600} "public final int com.sun.proxy.$Proxy164.hashCode()"

和 所有代理接口中的 所有方法。

代理对象的方法实现逻辑:比如 equals方法

return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

就是调用 

java.lang.reflect.InvocationHandler#invoke

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}

 第一个参数,代理对象本身

第二个参数,调用的代理的接口中的方法

第三个参数,调用的方法 入参

JDK动态代理 需要开发者关注的,就是 实现 InvocationHandler 接口。实际 也并不需要很复杂的实现,可以引用另一个服务来实现。

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator

在spring 实例化 bean之后,会执行

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

完成aop 代理对象的生成。

JDK动态代理的 InvocationHandler 的实现类为 JdkDynamicAopProxy,其增强功能由 advised 完成。和上面画的结构 基本一致。

Advisor

寻找Advisor

// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

第一部分,直接查找所有的 Advisor类型的bean

this.beanFactory.getBean(name, Advisor.class)

第二部分,查询标有 @Aspect 注解的 bean

private boolean hasAspectAnnotation(Class<?> clazz) {
		return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
	}

 针对每个bean,找到所有 没有标记 @Pointcut 注解的方法

对每个方法查找对应的切面,标记 标记 @Around等注解的方法

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
			Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

protected enum AspectJAnnotationType {

		AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing
}

获取 注解的value值,通常返回的是 标记有 @Pointcut 注解的 方法名

Method method = annotation.annotationType().getDeclaredMethod(attributeName);
ReflectionUtils.makeAccessible(method);
return method.invoke(annotation);

开始包装该切面方法:

关键代码块

switch (aspectJAnnotation.getAnnotationType()) {
			case AtPointcut:
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				}
				break;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		}

根据切面的类型,返回不同的Advice类型的对象。

寻找可以应用在当前bean的 advisor。这个逻辑有点复杂,先跳过

return canApply(pca.getPointcut(), targetClass, hasIntroductions);

 

接下来就是创建代理对象

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

这边代码看起来绕来绕去,实际设计的比较精炼。使用一个工厂类的一个方法就返回了代理对象。

proxyFactory.getProxy(getProxyClassLoader());

createAopProxy().getProxy(classLoader)

getAopProxyFactory().createAopProxy(this)

多个方法级联调用,只是这里进行了 一个拆分。

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}

 

上面描述了,JdkDynamicAopProxy就是 InvocationHandler的实现类。

其invoke方法内部 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 完了对切面的执行调用。

执行过程:

 

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

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

相关文章

Android 遥控器

遥控器源码 import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RadialGradient; import android.graphics.Region; import android.g…

JavaScript(2)——输入输出和执行顺序

目录 JS的输入输出语法 输出&#xff1a; 输入 JS的代码执行顺序 字面量 JS的输入输出语法 输出&#xff1a; document.write(内容)alert(内容) 页面弹出警告框console.log(内容) 控制台输出语法&#xff0c;程序员调试使用 作用&#xff1a;向body输出内容 注意&…

Node.js简介

一&#xff1a;Node.js简介 Node.js是一个跨平台的JavaScript运行环境&#xff0c;使开发者可以搭建服务器端的JavaScript应用程序 作用&#xff1a;使用Node.js编写服务器端程序 编写数据接口&#xff0c;提供网页资源浏览功能有利于前端工程化&#xff0c;可以集成各种开发…

等保测评练习卷14

等级保护初级测评师试题14 姓名&#xff1a; 成绩&#xff1a; 判断题&#xff08;10110分&#xff09; 1. 方案编制活动中测评对象确定、测评指…

【python】OpenCV—Aruco

文章目录 Detect ArucoGuess Aruco Type Detect Aruco 学习参考来自&#xff1a;OpenCV基础&#xff08;19&#xff09;使用 OpenCV 和 Python 检测 ArUco 标记 更多使用细节可以参考&#xff1a;【python】OpenCV—Color Correction 源码&#xff1a; 链接&#xff1a;http…

在 UBUNTU 22.04 上逐步构建 Postal SMTP 服务器

构建 Postal SMTP 服务器来发送批量电子邮件是电子邮件营销人员的不错选择。Postal 功能非常强大&#xff0c;并拥有大量开发人员的支持。它是一个用 JavaScript 和 Ruby 编写的开源邮件服务器脚本。它可用于构建内部 SMTP 服务器&#xff0c;就像 Mailgun、Sendgrid、Mailchim…

【期末速成】计算机操作系统 EP03 | 学习笔记

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点五&#xff1a;进程的概念及特征2.1 考点六&#xff1a;进程的状态与切换 三、总结&#xff1a;&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f6…

Why is Kafka fast?(Kafka性能基石)

Kafka概述 Why is kafka fast&#xff1f; 思考一下&#xff0c;当我们在讨论Kafka快的时候我们是在谈论什么呢&#xff1f;What does it even mean that Kafka is fast? 我们是在谈论kafka的低延迟&#xff08;low latency&#xff09;还是在讨论吞吐量&#xff08;through…

快速了解GPT-4o和GPT-4区别

GPT-4o简介 在5月14日的OpenAI举行春季发布会上&#xff0c;OpenAI在活动中发布了新旗舰模型“GPT-4o”&#xff01;据OpenAI首席技术官穆里穆拉蒂&#xff08;Muri Murati&#xff09;介绍&#xff0c;GPT-4o在继承GPT-4强大智能的同时&#xff0c;进一步提升了文本、图像及语…

C语言 | Leetcode C语言题解之第204题计数质数

题目&#xff1a; 题解&#xff1a; int countPrimes(int n) {if (n < 2) {return 0;}int isPrime[n];int primes[n], primesSize 0;memset(isPrime, 0, sizeof(isPrime));for (int i 2; i < n; i) {if (!isPrime[i]) {primes[primesSize] i;}for (int j 0; j < …

TCP单进程循环服务器程序与单进程客户端程序

实验目的 理解并掌握以下内容: 网络进程标识(即套接字地址)在Linux中的数据结构与地址转换函数。网络字节序与主机字节序的定义、转换以及相关函数在网络编程中的应用。数据结构内存对齐的基本规则,以及基于数据结构构建PDU的基本方法。TCP单进程循环服务器与单进程客户端的…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-44目标检测算法综述:R-CNN、SSD和YOLO

41~44目标检测算法综述&#xff1a;R-CNN、SSD和YOLO 1. 区域卷积神经网络 (R-CNN 系列) 1.1 R-CNN 使用启发式搜索算法来选择锚框。使用预训练模型对每个锚框提取特征&#xff08;每个锚框视为一张图片&#xff0c;使用 CNN 提取特征&#xff09;。训练 SVM 进行类别分类&a…

ONLYOFFICE 8.1 版本桌面编辑器测评

在现代办公环境中&#xff0c;办公软件的重要性不言而喻。从文档处理到电子表格分析&#xff0c;再到演示文稿制作&#xff0c;强大且高效的办公软件工具能够极大提升工作效率。ONLYOFFICE 作为一个功能全面且开源的办公软件套件&#xff0c;一直以来都受到广大用户的关注与喜爱…

[物联网专题] - 螺钉式接线端子的选择和辨识

工业设备上大量使用各式各样的端子来连接外部设备和电缆电线&#xff0c;其中用得最多的就是标准的螺钉式端子&#xff0c;其外形如下&#xff1a; 标准端子一般是2位&#xff08;2个接线端子&#xff09;&#xff0c;端子与端子之间可以级联&#xff0c;组成任意数量的位数。…

SpringCloudAlibaba基础四 微服务调用组件OpenFeign

JAVA 项目中如何实现接口调用&#xff1f; 1&#xff09;Httpclient HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 …

Databend db-archiver 数据归档压测报告

Databend db-archiver 数据归档压测报告 背景准备工作Create target databend table启动 small warehouse准备北京区阿里云 ECSdb-archiver 的配置文件准备一亿条源表数据开始压测 背景 本次压测目标为使用 db-archiver 从 MySQL 归档数据到 Databend Cloud&#xff0c; 归档的…

2006年下半年软件设计师【上午题】试题及答案

文章目录 2006年下半年软件设计师上午题--试题2006年下半年软件设计师上午题--答案2006年下半年软件设计师上午题–试题

idea 项目互联网转内网开发 依赖报错问题 maven问题

场景&#xff1a; 这个问题困扰好久&#xff0c;通过分析后&#xff0c;发现是maven配置问题&#xff0c;废话不多说&#xff0c;上干活。 问题描述 项目互联网从转内网开发&#xff0c;提前下载好repository&#xff0c;跟项目一起导入内网&#xff0c;导入后&#xff0c;发…

昇思25天学习打卡营第10天|FCN图像语义分割

一、简介&#xff1a; 本篇博客是昇思大模型打卡营应用实践部分的第一次分享&#xff0c;主题是计算机视觉&#xff08;CV&#xff09;领域的FCN图像语义分割&#xff0c;接下来几天还会陆续分享其他CV领域的知识&#xff08;doge&#xff09;。 全卷积网络&#xff08;Fully…

linux-内存映射MMAP-lseek-dup-fifo-通信-IO多路复用

1、内存映射MMap&#xff1a; DMA&#xff1a; 可以用*/[]取代read和write&#xff1b; 限制&#xff1a; 1、文件大小固定不能改变&#xff1b;&#xff08;ftruncate&#xff09; 2、只能是磁盘文件&#xff1b; 3、建立映射之前先open mmap函数&#xff1a; mmap第一个…