聊聊Spring中循环依赖与三级缓存

news2025/4/16 17:12:42

先看几个问题

  • 什么事循环依赖?
  • 什么情况下循环依赖可以被处理?
  • spring是如何解决循环依赖的?

什么是循环依赖?

简单理解就是实例 A 依赖实例 B 的同时 B 也依赖了 A

在这里插入图片描述

@Component
public class A {
	// A 中依赖 B
	@Autowired
	private B b;
}

@Component
public class B {
	// B 中依赖 A
	@Autowired
	private A a;
}

什么情况下循环依赖可以被处理?

spring 解决循环依赖是有前提条件的

  • 出现循环依赖的 bean 必须是单例的
  • 依赖注入的方式不能全是构造器注入的方式

其中第一点是很好理解的,第二点:不能全是构造器注入是什么意思呢?用代码说话

@Component
public class A {
	// A 中依赖 B
	public A(B b){
	}
}

@Component
public class B {
	// B 中依赖 A
	public B(A a){
	}
}

为了测试循环依赖的解决情况跟注入方式的关系,我们做如下四种情况的测试
在这里插入图片描述

Spring是如何解决循环依赖的?

分两种情况进行说明:

  • 简单的循环依赖(没有AOP)
  • 含有AOP的循环依赖
简单的循环依赖(没有AOP)

还是使用上面的例子:

@Component
public class A {
	// A 中依赖 B
	@Autowired
	private B b;
}

@Component
public class B {
	// B 中依赖 A
	@Autowired
	private A a;
}

通过前面我们知道这种循环依赖是可以解决的,下面进行分析:

首先我们都知道Spring在创建Bean的时候主要有三步:

  • 实例化,对应方法 AbstractAutowireCapableBeanFactory#createBeanInstance,实例化之后只是在堆中创建了实例,实例中属性都为默认值,然后放入到三级缓存之中
  • 属性注入,对应方法AbstractAutowireCapableBeanFactory#populateBean,为实例化之后的对象进行属性填充
  • 初始化,对应方法AbstractAutowireCapableBeanFactory#initializeBean,执行初始化方法,之后在实现了BeanPostProcessorpostProcessAfterInitialization完成AOP代理

在这里插入图片描述

创建A对象
getSingleton()
	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		// beanName 断言处理
		Assert.notNull(beanName, "Bean name must not be null");
		// 对一级缓存加锁处理
		synchronized (this.singletonObjects) {
			// 从一级缓存中获取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				// 一级缓存中获取不到
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				// 此处是在单例bean创建之前, 判断bean是否需要检查,
				// 并且将beanName添加到singletonsCurrentlyInCreation(正在创建bean的集合,是一个setFromMap集合)中
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 此处是一个回调,回去执行createBean()方法,也就是开始真正的创建bean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					// 至此,beanName创建完毕,从singletonsCurrentlyInCreation(正在创建的集合)中移除
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// 将成品的bean添加到一级缓存,并从二级缓存、三级缓存中移除,并添加到已完成注册的单例bean集合中
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

从上面我们可以看到,spring在创建一个bean时,先是调用 getBean() -> doGetBean() -> getSingleton() 主要的处理逻辑就在getSingleton()之中,从getSingleton()源码中我们可以看到

  • 1、先从一级缓存获取A
  • 2、获取不到,再执行回调去创建A

下面接着调用回调方法 createBean() 去创建 A

大致流程如下:本质就是使用反射创建A对象实例

在这里插入图片描述
注意:需要注意在 createBeanInstance() 方法中先调用 instantiateBean() 方法创建bean实例对象,创建完毕以后,会接着调用 addSingletonFactory()方法,下面我们分析一下这个方法

addSingletonFactory

在这里插入图片描述

可以看到 earlySingletonExposure 为 true,就会将 创建出来的A对象实例对象放入到三级缓存之中

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		// 对 singletonFactory 进行非空校验
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		// 对一级缓存进行加锁
		synchronized (this.singletonObjects) {
			// 如果一级缓存中不存在 beanName 对应的单例对象
			if (!this.singletonObjects.containsKey(beanName)) {
				// 将 singletonFactory 添加到三级缓存中
				this.singletonFactories.put(beanName, singletonFactory);
				// 将 beanName 从二级缓存中移除
				this.earlySingletonObjects.remove(beanName);
				// 将 beanName 添加到 registeredSingletons 中
				this.registeredSingletons.add(beanName);
			}
		}
	}

可以看出这里放入三级缓存中的是一个 ObjectFactory ,这个工厂的 getObject()方法可以得到一个对象,而这个对象是由 getEarlyBeanReference() 创建的,那么问题来了,这个 getEarlyBeanReference() 方法什么时候被调用呢?在创建B对象的时候

接着往下看:

三级缓存放入完毕,然后对A进行属性填充,大致流程如下,这里不做详细分析

在这里插入图片描述
一句话概括就是:对A进行属性填充的时候发现,A中依赖了B对象,就调用 this.beanFactory.getBean()方法,获取B对象实例,也就是套娃模式开启

又开始重复上述流程去创建B对象实例,流程如下:

在这里插入图片描述
此时 B 对象创建完毕,三级缓存中也有了 B 对象的工厂,然后对 B 对象进行属性填充,流程与A类似,属性填充过程中发现B对象中依赖A对象,又调用 this.beanFactory.getBean(A),下面分析getSingleton()方法
注意:此处 getSingleton()方法与前面介绍的getSingleton()方法不同,前面介绍的getSingleton()方法是在本次getSingleton()方法执行完毕未获取到结果之后,才会执行前面讲解的getSingleton()方法

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		// 从一级缓存中获取该beanName实例
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果以及缓存中不存在并且该beanName对应的单例bean正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			// 从二级缓存中获取该beanName实例
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 二级缓存中不存在并且允许提前引用
			if (singletonObject == null && allowEarlyReference) {
				// 锁定全局变量进行操作
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					// 从一级缓存中获取该beanName实例
					singletonObject = this.singletonObjects.get(beanName);
					// 如果一级缓存中获取不到
					if (singletonObject == null) {
						// 从二级缓存中获取
						singletonObject = this.earlySingletonObjects.get(beanName);
						// 二级缓存中也获取不到
						if (singletonObject == null) {
							// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的 ObjectFactory 初始化策略存储在 singletonFactories
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								// 如果存在单例对象工厂,则通过工厂创建一个单例对象
								singletonObject = singletonFactory.getObject();
								// 放入到二级缓存中
								this.earlySingletonObjects.put(beanName, singletonObject);
								// 并从三级缓存中移除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

从源码中可以看到:getSingleton()方法会先从一级缓存中获取A对象实例,如果获取不到再从二级缓存中获取,二级缓存中获取不到再从三级缓存中,那么此时三级缓存中肯定是可以获取到的,获取到之后调用 getObject() 方法,此时调用 getObject()方法,会回调 getEarlyBeanReference() 方法

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		// 该方法主要用于提前获取 bean 的引用,以便于解决循环依赖的问题
		// 将当前 bean 赋值给 exposedObject
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			// 这块代码是用代理对象替换原始对象,这样就可以在原始对象的基础上做一些增强操作
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				// AOP --> AnnotationAwareAspectJAutoProxyCreator
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

从源码可以看出实际上就是调用了后置处理器的 getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy 注解导入的 AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		return exposedObject;
	}

这样的话,也就是说这个工厂啥也没干,直接将实例化阶段创建的对象返回了!

所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用,我直接将这个对象放到二级缓存中不是一点问题都没有吗?如果你说它提高了效率,那你告诉我提高的效率在哪?

那么三级缓存到底有什么作用呢?不要急,我们先把整个流程走完,在下文结合AOP分析循环依赖的时候你就能体会到三级缓存的作用!

到这里不知道小伙伴们会不会有疑问,B中提前注入了一个没有经过初始化的A类型对象不会有问题吗?

答:不会

这个时候我们需要将整个创建A这个Bean的流程走完,如下图:
在这里插入图片描述

从上图中我们可以看到,虽然在创建B时会提前给B注入了一个还未初始化的A对象,但是在创建A的流程中一直使用的是注入到B中的A对象的引用,之后会根据这个引用对A进行初始化,所以这是没有问题的。

创建B对象

从前面我们已经知道,在创建A的过程中已经把B对象创建好了,而且已经放入到了一级缓存,但是spring是通过循环遍历beanName去创建bean实例的,所以B还会在创建一次,与创建A对象的区别在于,在创建B对象的过程中在调用getSingleton()方法的时候,可以从一级缓存中直接拿到B对象,所以直接返回,不在进行创建

至此没有AOP的循环依赖就到此为止,下面继续看有AOP的循环依赖

AOP循环依赖

之前我们已经说过了,在普通的循环依赖的情况下,三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP相关,我们再来看一看getEarlyBeanReference()方法的代码:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		// 该方法主要用于提前获取 bean 的引用,以便于解决循环依赖的问题
		// 将当前 bean 赋值给 exposedObject
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			// 这块代码是用代理对象替换原始对象,这样就可以在原始对象的基础上做一些增强操作
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				// AOP --> AnnotationAwareAspectJAutoProxyCreator
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

如果在开启AOP的情况下,就会调用 AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference()方法

	public Object getEarlyBeanReference(Object bean, String beanName) {
		// 根据bean的类型和名称获取缓存的key,如果beanName为空,则使用bean的类型作为key
		// 如果beanName不为空,则使用beanName作为key,如果beanName是一个FactoryBean的名称,则使用&+beanName作为key
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		// 添加到earlyProxyReferences中
		this.earlyProxyReferences.put(cacheKey, bean);
		// 创建aop代理
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

从代码可以看出如果我们对A进行了AOP代理的话,那么此时的getEarlyBeanReference()方法将返回一个A的代理对象,而不是实例化阶段创建的A对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

在这里插入图片描述

看到这个图你可能会产生下面这些疑问

在给B注入的时候为什么要注入一个代理对象?

答:当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象

明明初始化的时候是A对象,那么Spring是在哪里将代理对象放入到容器中的呢?

在完成初始化的时候,spring会在调用一次getSingleton()方法,这一次传入的参数又不一样了,false可以理解为禁用三级缓存,前面说过,B进行属性填充的时候,已经从三级缓存中获取到A对象,然后生成A的代理对象,并将代理对象放入到二级缓存中,所以在A完成初始化的时候,所以再从二级缓存中获取到A代理对象赋值给 exposedObject,最终放入到一级缓存中
在这里插入图片描述
初始化的时候是对A对象本身进行初始化,而容器中以及注入到B中的都是代理对象,这样不会有问题吗?

答:不会,这是因为不管是cglib代理还是jdk动态代理生成的代理类,内部都持有一个目标类的引用,当调用代理对象的方法时,实际会去调用目标对象的方法,A完成初始化相当于代理对象自身也完成了初始化

三级缓存为什么要使用工厂而不是直接使用引用?换而言之,为什么需要这个三级缓存,直接通过二级缓存暴露一个引用不行吗?

答:这个工厂的目的在于延迟创建对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象

我们思考一种简单的情况,就以单独创建A为例,假设AB之间现在没有依赖关系,但是A被代理了,这个时候当A完成实例化后还是会进入下面这段代码:

        // Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			// 将创建的bean 的 lambda 表达式放入到三级缓存中
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

假设我们在这里直接使用二级缓存的话,那么意味着所有的Bean在这一步都要完成AOP代理。这样做有必要吗?

不仅没有必要,而且违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

三级缓存真的提高了效率了吗?

通过以上分析,我们已经知道了三级缓存的真正作用,但是这个答案可能还无法说服你,所以我们再最后总结分析一波,三级缓存真的提高了效率了吗?分为两点讨论:

没有进行AOP的Bean间的循环依赖

从上文分析可以看出,这种情况下三级缓存根本没用!所以不会存在什么提高了效率的说法

进行了AOP的Bean间的循环依赖

就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

在这里插入图片描述

假设不使用三级缓存直接使用二级缓存
在这里插入图片描述

上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的

综上,不管是哪种情况,三级缓存提高了效率这种说法都是错误的!

总结

“Spring是如何解决的循环依赖?”

答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

“为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?”

答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

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

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

相关文章

【表格插入小计行】el-table表格,数组对象中根据某字段插入小计行计算数据

前言 功能解释&#xff1a;遇到的一个需求&#xff0c;是表格的tabledata数组。里面有科室医生还有很多消费指标等数据。然后需要我排序后把科室放在一起。然后在每个科室下面添加一行数据&#xff0c;是小计行。用于计算上面相同科室的所有数据汇总。然后最下面再来个合计行&…

【深度学习实验】前馈神经网络(四):自定义逻辑回归模型:前向传播、反向传播算法

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 逻辑回归Logistic类 a. 构造函数__init__ b. __call__(self, x)方法 c. 前向传播forward d. 反向传播backward 2. 模型训练 3. 代码整合 一、实验介绍 实现逻…

JavaWeb 学习笔记 5:JSP

JavaWeb 学习笔记 5&#xff1a;JSP 简单的说&#xff0c;JSP 就是 Java Html&#xff0c;JSP 的出现是为了让 Java Web 应用生成动态页面更容易。 1.快速开始 1.1.依赖 添加 JSP 依赖&#xff1a; <dependency><groupId>javax.servlet.jsp</groupId>&…

华为云云耀云服务器L实例评测|使用docker部署禅道系统

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 文章目录 前言准备工作华为云账号注册充值、购买服务器 服务器操作密码修改登录远程工具 禅道部署简介 部署…

【校招VIP】java语言考点之序列化

考点介绍&#xff1a; 将java对象转换为字节序列的过程称为对象的序列化。对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上&#xff0c;通常存放在一个文件中。 2) 在网络上传送对象的字节序列。 java语言考点之序列化-相关题目及解析内容可点击文章末尾链…

PyCharm:No Python interpreter configured for the project

一、问题概述 Your 的 Pycharm 软件创建完项目后&#xff0c;结果无法运行&#xff0c;观察后&#xff0c;在Pycharm代码编辑区上面出现了这样的一个黄色条提示&#xff1a;No Python interpreter configured for the project 【问题】在您的Python项目中无Python解释器…

金融业需要的大模型,是一个系统化工程

今年年初&#xff0c;在AIGC刚刚开始爆火的时候&#xff0c;我们曾经采访过一位AI领域的专家。当我们提问哪个行业将率先落地大模型时&#xff0c;他毫不犹豫地说道&#xff1a;“金融。” 金融行业场景多、数据多、知识多&#xff0c;这样的“三多”特点让其成为AI大模型发挥价…

yarn安装依赖时报错 error An unexpected error occurred:

一切起因是因为前一天安装了volta管理node&#xff0c;第二天启动项目&#xff0c; 显示error An unexpected error occurred: “https://registry.npmmirror.com/webpack-aliyun-oss/-/webpack-aliyun-oss-0.2.6.tgz: Request failed “404 Not Found””. 项目启动时发现报错…

Selenium Grid 的搭建方法

传统 Selenium Grid 的搭建方法 搭建一个具有 1 个 Node 的 Selenium Grid。那么通常来讲我们需要 2 台机器&#xff0c;其中一台作为 Hub&#xff0c;另外一台作为 Node&#xff0c;并要求这两台机器已经具备了 Java 执行环境。 1.通过官网下载 selenium-server-standalone-…

Java 21 发布,新功能助力开发更高效

Java 21 是 Java SE 平台的最新长期支持 (LTS) 版本&#xff0c;于 2023 年 9 月 19 日发布。它包括了一系列新功能和改进&#xff0c;可以让开发人员编写更高效、更可靠、更安全的 Java 应用程序。 新功能亮点 Java 21 的新功能包括&#xff1a; 虚拟线程&#xff1a;虚拟线程…

【 Tkinter界面-练习05】 event和bind

一、说明 事件和动作有关&#xff1b;所有的界面都与运动有关&#xff0c;本篇将对事件、事件触发、绑定回调函数等&#xff0c;其实是一系列部件配合的复杂的过程&#xff0c;这些过程牵扯到系统如何设计&#xff0c;线程、消息队列循环等。本篇将详细介绍各种因素的关系。 二…

BCC源码编译和安装

接前一篇文章&#xff1a;BCC源码下载 1. 进入源码根目录 进入到BCC源码根目录。命令及结果如下&#xff1a; $ cd bcc ~/eBPF/BCC/bcc$ ls cmake CONTRIBUTING-SCRIPTS.md docs images libbpf-tools man scripts src CMakeLists.txt …

Matlab论文插图绘制模板第115期—带Latex公式的图

之前的文章中&#xff0c;分享了Matlab带线标记的图&#xff1a; 带阴影标记的图&#xff1a; 带箭头标记的图&#xff1a; 带图形标记的图&#xff1a; 进一步&#xff0c;分享一下带Latex公式的图&#xff0c;先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『…

http1和http2的主要区别

主要有四个方面&#xff1a; 二进制分帧多路复用服务器主动推送头部压缩 将前两点结合来说&#xff0c;首先 二进制分帧 帧&#xff1a;HTTP/2 数据通信的最小单位&#xff1b; 消息&#xff1a;HTTP/2 中&#xff0c;例如在请求和响应等操作中&#xff0c;消息由一个或多个…

赛宁党支部赴延安开展革命旧址学习主题党日活动

为深入学习贯彻新时代中国特色社会主义思想和中共二十大精神&#xff0c;不断提升党支部成员综合素质和业务能力&#xff0c;2023年9月&#xff0c;赛宁公司党支部组织北京、南京、广州等三地部分党员及入党积极分子开展了“革命旧址学习”主题党日活动&#xff0c;深入寻访延安…

TongWeb8下应用忙碌线程监控

问题 &#xff1a; 在系统运行过程中发现TongWeb进程占用CPU过高&#xff0c;需要分析是应用哪里引起的问题。 分析过程(仅限Linux环境)&#xff1a; 1. 通过top命令查看TongWeb的java进程占用的CPU情况。 查看误区&#xff1a;不要以为java进程CPU占到398%就是高&#xff0…

Java基于微信小程序的青少年健康心理科普平台

第一章 简介 青少年心理健康科普平台为用户提供心理医生咨询服务&#xff0c;系统包括微信小程序端和后台。 微信小程序用户可以先进行注册&#xff0c;填写个人的基本信息提交到服务器&#xff0c;服务器把数据保存到数据库。管理员对青少年的信息进行验证后&#xff0c;青少…

面试官:Javscript数组的常用方法有哪些?

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、操作方法 增 push() unshift() splice concat() 删 pop() shift() splice() slice() 改 splice() …

uview组件库的安装

更多的请查看官方文档uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com) // 如果您的根目录没有package.json文件的话&#xff0c;请先执行如下命令&#xff1a; // npm init -y 安装 npm install uview-ui2.0.36 // 更新 // npm update uvie…

生产数据追溯产线管理看板助力企业实现产品质量追踪

在现代制造业中&#xff0c;企业对于产品质量的追踪和管理变得越来越重要。产品质量的好坏直接关系到企业的声誉和客户满意度。然而&#xff0c;传统的生产管理方式往往无法提供足够的数据和信息来进行全面的质量追踪。生产看板管理系统的出现为企业解决了这一难题。通过实时的…