Spring-ProxyFactory

news2024/11/16 9:27:36

ProxyFactory选择cglib或jdk动态代理原理

ProxyFactory在生成代理对象之前需要决定是使用JDK动态代理还是CGLIB技术:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快
		// 或者isProxyTargetClass为true,
		// 或者被代理对象没有实现接口,
		// 或者只实现了SpringProxy这个接口
		// 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理

		// 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口

		// 是不是在GraalVM虚拟机上运行
		if (!NativeDetector.inNativeImage() &&
				(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
			// config就是ProxyFactory对象
			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.");
			}
            // targetClass是接口,直接使用Jdk动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
            // 使用Cglib
			return new ObjenesisCglibAopProxy(config);
		}
		else {
            // 使用Jdk动态代理
			return new JdkDynamicAopProxy(config);
		}
	}

	/**
	 * Determine whether the supplied {@link AdvisedSupport} has only the
	 * {@link org.springframework.aop.SpringProxy} interface specified
	 * (or no proxy interfaces specified at all).
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}
}

代理对象创建过程

JdkDynamicAopProxy

/**
 * Construct a new JdkDynamicAopProxy for the given AOP configuration.
 * @param config the AOP configuration as AdvisedSupport object
 * @throws AopConfigException if the config is invalid. We try to throw an informative
 * exception in this case, rather than let a mysterious failure happen later.
 */
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
	Assert.notNull(config, "AdvisedSupport must not be null");
	if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
		throw new AopConfigException("No advisors and no TargetSource specified");
	}
	this.advised = config;
	// 设置JDK动态代理所要代理的接口
	this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}

// 1、在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,
// 并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],
// 并赋值给proxiedInterfaces属性
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
	// 被代理对象自己所实现的接口
	Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();

	// 如果被代理对象没有实现接口,则判断被代理类是不是接口,或者被代理类是不是已经经过JDK动态代理之后的类从而获取想对应的接口
	if (specifiedInterfaces.length == 0) {
		// No user-specified interfaces: check whether target class is an interface.
		Class<?> targetClass = advised.getTargetClass();
		if (targetClass != null) {
			if (targetClass.isInterface()) {
				advised.setInterfaces(targetClass);
			}
			else if (Proxy.isProxyClass(targetClass)) {
				advised.setInterfaces(targetClass.getInterfaces());
			}
			specifiedInterfaces = advised.getProxiedInterfaces();
		}
	}

	// 添加三个Spring内置接口:SpringProxy、Advised、DecoratingProxy
	List<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);
	for (Class<?> ifc : specifiedInterfaces) {
		// Only non-sealed interfaces are actually eligible for JDK proxying (on JDK 17)
		if (isSealedMethod == null || Boolean.FALSE.equals(ReflectionUtils.invokeMethod(isSealedMethod, ifc))) {
			proxiedInterfaces.add(ifc);
		}
	}
	if (!advised.isInterfaceProxied(SpringProxy.class)) {
		proxiedInterfaces.add(SpringProxy.class);
	}
	if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {
		proxiedInterfaces.add(Advised.class);
	}
	if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {
		proxiedInterfaces.add(DecoratingProxy.class);
	}
	return ClassUtils.toClassArray(proxiedInterfaces);
}

// 2、检查这些接口中是否定义了equals()、hashcode()方法
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
	// 判断被代理的接口中是否定义了equals()、hashCode()方法,
    // 如果在接口中手动定义了这两个方法,则也会进行代理
	// 否则这两个方法是不会走代理逻辑的
	for (Class<?> proxiedInterface : proxiedInterfaces) {
		Method[] methods = proxiedInterface.getDeclaredMethods();
		for (Method method : methods) {
			if (AopUtils.isEqualsMethod(method)) {
				this.equalsDefined = true;
			}
			if (AopUtils.isHashCodeMethod(method)) {
				this.hashCodeDefined = true;
			}
			if (this.equalsDefined && this.hashCodeDefined) {
				return;
			}
		}
	}
}

// 3、得到代理对象,JdkDynamicAopProxy作为InvocationHandler
// 代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的invoke()方法中
public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
	}
	// this实现了InvocationHandler
	return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

ObjenesisCglibAopProxy

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
	}

	try {
		// 被代理的类
		Class<?> rootClass = this.advised.getTargetClass();
		Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

		Class<?> proxySuperClass = rootClass;
		// 0、如果被代理类本身就已经是Cglib所生成的代理类了
		if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
			// 获取真正的被代理类
			proxySuperClass = rootClass.getSuperclass();
			// 获取被代理类所实现的接口
			Class<?>[] additionalInterfaces = rootClass.getInterfaces();
			for (Class<?> additionalInterface : additionalInterfaces) {
				this.advised.addInterface(additionalInterface);
			}
		}

		// Validate the class, writing log messages as necessary.
		validateClassIfNecessary(proxySuperClass, classLoader);

		// Configure CGLIB Enhancer...
        // 1、创建Enhancer对象
		Enhancer enhancer = createEnhancer();
		if (classLoader != null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader &&
					((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
				enhancer.setUseCache(false);
			}
		}

		// 2、被代理类,代理类的父类。proxySuperClass就是ProxyFactory.setTarget()所设置的对象的类
		enhancer.setSuperclass(proxySuperClass);
		// 3、代理类额外要实现的接口。通过ProxyFactory.addInterface()所添加的接口,
        // 以及SpringProxy、Advised、DecoratingProxy接口
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

		// 获取和被代理类所匹配的Advisor
		Callback[] callbacks = getCallbacks(rootClass);
		Class<?>[] types = new Class<?>[callbacks.length];
		for (int x = 0; x < types.length; x++) {
			types[x] = callbacks[x].getClass();
		}
		// fixedInterceptorMap only populated at this point, after getCallbacks call above
		enhancer.setCallbackFilter(new ProxyCallbackFilter(
				this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);

		// Generate the proxy class and create a proxy instance.
        // 4、设置Enhancer的Callbacks为DynamicAdvisedInterceptor
        // 代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中
		return createProxyClassAndInstance(enhancer, callbacks);
	}
	catch (CodeGenerationException | IllegalArgumentException ex) {
		throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
				": Common causes of this problem include using a final class or a non-visible class",
				ex);
	}
	catch (Throwable ex) {
		// TargetSource.getTarget() failed
		throw new AopConfigException("Unexpected AOP exception", ex);
	}
}

代理对象执行过程

1、在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor

2、代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选

3、把和方法所匹配的Advisor适配成MethodInterceptor

4、把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象

5、调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法

6、按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法

7、直到执行完最后一个MethodInterceptor,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法

各注解对应的MethodInterceptor

@Before对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor

  • 先执行advice对应的方法

  • 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

@After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 再执行advice对应的方法

@Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor

  • 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用

@AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 如果上面抛了Throwable,那么则会执行advice对应的方法

@AfterReturning对应的是AspectJAfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 执行上面的方法后得到最终的方法的返回值

  • 再执行advice对应的方法

AbstractAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator的父类是AbstractAdvisorAutoProxyCreator。 AbstractAdvisorAutoProxyCreator非常强大以及重要,只要Spring容器中存在这个类型的Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator实际上就是一个BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中。

当某个Bean初始化后,会调用wrapIfNecessary()方法进行AOP:AbstractAdvisorAutoProxyCreator会找到所有的Advisor,然后判断当前这个Bean是否存在某个Advisor与之匹配(根据Pointcut),如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行AOP,需要产生一个代理对象。

@EnableAspectJAutoProxy

主要作用:往Spring容器中添加了一个AnnotationAwareAspectJAutoProxyCreator类型的Bean

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法。

AbstractAdvisorAutoProxyCreator只能找到所有Advisor类型的Bean对象。

AspectJAwareAdvisorAutoProxyCreator除了可以找到所有Advisor类型的Bean对象,还能把@Aspect注解所标注的Bean中的@Before等注解及方法进行解析,并生成对应的Advisor对象。

简单理解:@EnableAspectJAutoProxy注解就是往Spring容器中添加了一个AbstractAdvisorAutoProxyCreator类型的Bean,从而开启了AOP,并且还会解析@Before等注解生成Advisor。

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

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

相关文章

59基于matlab的爬行动物搜索算法(Reptile search algorithm, RSA)

基于matlab的爬行动物搜索算法&#xff08;Reptile search algorithm, RSA&#xff09;一种新型智能优化算法。该算法主要模拟鳄鱼的捕食行为&#xff0c;来实现寻优求解&#xff0c;具有收敛速度快&#xff0c;寻优能力强的特点。程序已调通&#xff0c;可直接运行。 59matlab…

案例续集留言板

前端没有保存数据的功能,后端把数据保存下来(内存,数据库等等......) 前端代码如下 : <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initia…

Centos, RockyLinux 常用软件安装汇总

一、基本指令&#xff1a; 命令作用clear清屏pwd显示当前路径cat / more显示文本文档uname -a查看当前版本hostnamectl查看当前版本cat /etc/redhat-release查看当前版本free查看剩余内存df -h[查看磁盘剩余空间]du -sh 查看文件夹名"dir"占用的空间lsof -i:8080查看…

未来的拥塞控制与 Linux EEVDF 调度器

有破要有立。 前面提到 经典端到端拥塞控制将越来越失效&#xff0c;未来该如何&#xff0c;谈谈我的看法。 端到端拥塞控制的难点根本上是要解决公平性问题&#xff0c;顺带着提高资源利用率。我们很容易理解&#xff0c;在共享资源场景下&#xff0c;不公平一定是低效的&am…

在AutoDL云环境上训练Stable Diffusion Lora模型

AutoDL官网&#xff1a; AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDLAutoDL为您提供专业的GPU租用服务&#xff0c;秒级计费、稳定好用&#xff0c;高规格机房&#xff0c;7x24小时服务。更有算法复现社区&#xff0c;一键复现算法。https://www.autodl.com/ 新建实例…

优酷网页截图黑屏及了解浏览器图形服务API-meethigher

一、背景 周六跟同事逛了上海的豫园、城隍庙、静安寺、静安公园。豫园门票40&#xff0c;相传是明代私人园林&#xff0c;园主人为当年的四川布政使&#xff0c;是江南风格古典园林&#xff0c;风景还不错。 周日天气降温&#xff0c;直接睡了一天&#xff0c;想起同事推荐的《…

springboot项目使用Swagger3

一、Swagger介绍 号称世界上最流行的Api框架&#xff1b;Restful Api 文档在线自动生成工具>Api文档与API定义同步更新直接运行&#xff0c;可以在在线测试API 接口支持多种语言&#xff1a;&#xff08;java&#xff0c;Php…&#xff09; 二、Swagger3 准备工作 1、在p…

【文件IO】

文章目录 File常见方法和属性属性构造方法方法 InputStream方法FileInputStream OutputStream利用 OutputStreamWriter 进行字符写入 总结按字节读取数据按字节写入数据按字符读取数据按字符写入数据 File常见方法和属性 属性 修饰符及类型属性说明static StringpathSeparato…

JavaScript从入门到精通系列第三十五篇:JavaScript中的DOM简介

文章目录 前言 1&#xff1a;对象分类 2&#xff1a;宿主对象 一&#xff1a;DOM 1&#xff1a;dom简介 2&#xff1a;Dom概念图示 二&#xff1a;节点 1&#xff1a;节点概述 2&#xff1a;常用节点分类 3&#xff1a;节点模型示意图 4&#xff1a;节点属性 5&…

Java 之 IO/NIO/OKIO

BIO blocking io AIO Asynchronous IO 从内存读取到写入--输出 从外部到内存 -- 输入 OutputStream //文件不存在则自动创建 try {OutputStream outputStream new FileOutputStream("text.txt");outputStream.write(a);outputStream.write(b);} catch (IOExcep…

若依Linux与Docker集群部署

若依Linux集群部署 1. 若依2.MYSQL Linux环境安装2.1 MYSQL数据库部署和安装2.2 解压MYSQL安装包2.3 创建MYSQL⽤户和⽤户组2.4 修改MYSQL⽬录的归属⽤户2.5 准备MYSQL的配置⽂件2.6 正式开始安装MYSQL2.7 复制启动脚本到资源⽬录2.8 设置MYSQL系统服务并开启⾃启2.9 启动MYSQL…

终止进程后,GPU显存仍被占用问题 | kill -9彻底杀死进程 | ps aux|grep python

本文部分内容参考博客&#xff0c;十分感谢&#xff01;&#xff01;&#xff01; 问题描述&#xff1a;在Linux终端把进程终止后&#xff0c;发现显存没有被释放出来&#xff01; ---------------------------------------------------------------------------------------F…

Git分支与Git标签的介绍及其场景应用

目录 一、Git分支 1.1 定义 1.2 基本概念 1.3 特点与优势 1.4 Git分支操作命令 1.4.1 查看分支 1.4.2 创建分支 1.4.3 删除分支 1.4.4 切换分支 1.4.5 创建并切换到新建分支 1.5 场景应用 1.5.1 前期准备 1.5.2 具体操作 二、Git标签 2.1 定义 2.2 类型 2.3 标…

Some/IP学习笔记

目录 1.概述 2.SOME/IP 报文格式 3.数据结构序列化 1.概述 SOME/IP全称为Scalable Service Oriented MiddlewarE Over IP&#xff0c;是车载以太网技术中的核心内容&#xff0c;它为网络提供了面向服务的通信方式。一个服务可以包含0个或者多个事件&#xff08;events&#…

【C++ 学习 ㉟】- 异常详解

目录 一、C 异常处理的基本语法 1.1 - 抛出异常 1.2 - 检测和捕获异常 二、在函数调用链中异常栈展开的匹配原则 三、异常重新抛出 四、异常规范 五、C 标准异常体系 程序的错误大致可以分为以下三种&#xff1a; 语法错误&#xff1a;在编译和链接阶段就能发现&#xf…

Linux——vim简介、配置方案(附带超美观的配置方案)、常用模式的基本操作

vim简介、配置方案、常用模式的基本操作 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的xmind和.png文件都已同步导入至资源 1. vim简介 vim是Linux常用的文本编辑器&#xff0c;每个Linux账户都独有一个vim编辑器 本篇我们介绍vim最常用的三种模式&#xff1a;…

史上最全最新Ubuntu20.04安装教程(图文)

总的来说&#xff0c;安装Ubantu包含以下三个步骤&#xff1a; 一、安装虚拟机 二、Ubuntu镜像下载 三、虚拟机配置 一、安装虚拟机 选择安装VMware Workstation&#xff0c;登录其官网下载安装包&#xff0c;链接如下&#xff1a; 下载 VMware Workstation Pro​www.vmwa…

Linux中字符设备的打开、写入

一个内核模块应该由以下几部分组成。 第一部分&#xff0c;头文件部分。一般的内核模块&#xff0c;都需要 include 下面两个头文件&#xff1a; #include <linux/module.h> #include <linux/init.h> 第二部分&#xff0c;定义一些函数&#xff0c;用于处理内核…

【Python大数据笔记_day07_hive中的分区表、分桶表以及一些特殊类型】

分区表 分区表的特点/好处:需要产生分区目录,查询的时候使用分区字段筛选数据,避免全表扫描从而提升查询效率 效率上注意:如果分区表在查询的时候呀没有使用分区字段去筛选数据,效率不变 分区字段名注意:分区字段名不能和原有的字段名重复,因为分区字段名要作为字段拼接到表后…