Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(构造器方法)

序言

前面几篇文章介绍了Spring中几种方式下Bean对象的实例化的过程,那如果之前的几种都不满足,按照Spring中正常Bean的实例化步骤,该如何创建这个Bean对象呢?

测试类

我们先创建几个debug中用到的栗子。

Person
以一个平平无奇的Person对象的创建为切入点。

public class Person {
	// 省略了get set和构造方法,但是debug过程中构造方法很重要。
	private String name;
	private int age;
}	

person.xml
xml中bean标签配置了person对象信息,并且设置了scope = prototype以及<constructor-arg>。注释掉的<property>标签其实加载逻辑和<constructor-arg>是一样的,我们这里拿<constructor-arg>为例。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="person" class="org.springframework.factoryMethod.Person" scope="prototype">
		<bean id="person" class="org.springframework.factoryMethod.Person" scope="prototype">
<!--		<property name="age" value="22"/>-->
<!--		<property name="name" value="李四"/>-->
		<constructor-arg name="age" value="18"/>
		<constructor-arg name="name" value="张三"/>
	</bean>
	</bean>
</beans>

main

public class ConstructorTest {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("person.xml");
		Person p1 = (Person)ac.getBean("person");
		Person p2 = (Person)ac.getBean("person");
	}
}

所以根据我们在xml中的配置,在Person(p1)对象实例化中的流程大体如下:

  1. 获取构造器方法列表(因为类中可能定义很多的构造器方法)
  2. 遍历构造器,获取当前构造器方法的参数名称(name,age)
  3. 获取xml中配置的我们配置的具体属性值(18,张三)
  4. 将获取到的参数名称、值与构造器方法做匹配
  5. 计算构造器权重,确认最终使用的构造器
  6. 缓存当前所使用的的构造器方法,实例化Bean对象。

而当我们p2对象进行实例化时,就可以直接从缓存中进行获取。无需再重新遍历。

主流程方法

接下来我们回到doCreateBean的主流程方法中的createBeanInstance方法中,接着往下看。

doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		//这个beanWrapper是用来持有创建出来的bean对象的
		BeanWrapper instanceWrapper = null;

		//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息
		if (mbd.isSingleton()) {
			// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 没有就创建实例
		if (instanceWrapper == null) {
			// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//省略部分代码
		return exposedObject;
	}

createBeanInstance
debug时省略了之前提到的SupplierFactoryMethod创建方法的步骤,我们直接从boolean resolved = false;开始看。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 锁定class,根据设置的class属性或者根据className来解析class
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		// 如果beanClass != null
		// 并且,访问修饰符不是public修饰, 抛异常
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException;
		}
		// 获取Supplier
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		// 如果不为null,
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		//如果工厂方法不为null,则采用工厂方法来实例化
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		//标记下,防止重复创建一个Bean
		boolean resolved = false;
		//是否需要自动装配
		boolean autowireNecessary = false;
		//如果args为null,则进行同步锁,获取构造方法
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				//如果已经解析了构造方法或工厂方法
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		//如果已经解析了构造方法或工厂方法
		if (resolved) {
			//构造器有参数
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				//使用默认的无参构造
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		//从SmartInstantiationAwareBeanPostProcessor中确认我们的构造器
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		//获取首选的构造器,方法return null 自己自定义扩展
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
		// 如果没有特殊处理,则采用无参构造器来实例化对象
		return instantiateBean(beanName, mbd);
	}

此时来到了p1对象的创建,设置resolved等标志位后,因为此时缓存中resolvedConstructorOrFactoryMethod == null,所以并不会进到下面的判断。
在这里插入图片描述

此时resolvedConstructorOrFactoryMethod == null,并不会修改resolved的值,等到下面的 if 判断,因为在xml中设置了构造器参数,所以此时mbd.hasConstructorArgumentValues()返回true,能够进到判断中,接下来我们看autowireConstructor方法。
在这里插入图片描述
而我们的atowireMode共有5种,我们这里没有配置,所以no。
在这里插入图片描述

autowireConstructor

protected BeanWrapper autowireConstructor(
			String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {

		return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
	}

创建Bean实例的整体流程。

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

		//创建BeanWrapperImpl对象
		BeanWrapperImpl bw = new BeanWrapperImpl();
		//初始化bw (设置ConversionService和工厂中所有的PropertyEditor) 用来属性转换
		this.beanFactory.initBeanWrapper(bw);


		Constructor<?> constructorToUse = null;
		ArgumentsHolder argsHolderToUse = null;
		Object[] argsToUse = null;
		//如果有传入的参数,则直接使用
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
		else {
			//声明一个要解析的args数组,默认为null
			Object[] argsToResolve = null;
			synchronized (mbd.constructorArgumentLock) {
				//将beanDefinition中解析完的构造函数和参数
				constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
				//BeanDefinition中存在解析过的构造函数和参数 && 存在构造函数参数
				if (constructorToUse != null && mbd.constructorArgumentsResolved) {
					// Found a cached constructor...
					//从缓存中找到了构造器,继续从缓存中找准备好的构造器参数
					argsToUse = mbd.resolvedConstructorArguments;
					if (argsToUse == null) {
						argsToResolve = mbd.preparedConstructorArguments;
					}
				}
			}
			// 如果缓存中存在解析好的参数,解析配置的参数
			if (argsToResolve != null) {
				argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
			}
		}
		//如果缓存中没有解析好的构造器函数 || 参数构造器参数为空
		if (constructorToUse == null || argsToUse == null) {
			// Take specified constructors, if any.
			// 如果传入的chosenCtors不为null,则使用chosenCtors,否则通过反射获取类中定义的构造器函数
			Constructor<?>[] candidates = chosenCtors;
			if (candidates == null) {
				Class<?> beanClass = mbd.getBeanClass();
				try {
					candidates = (mbd.isNonPublicAccessAllowed() ?
							beanClass.getDeclaredConstructors() : beanClass.getConstructors());
				}
				catch (Throwable ex) {
					throw new BeanCreationException;
				}
			}
			//如果只有1个候选的构造器函数 && 传入参数explicitArgs = null && mbd中也没有构造器函数参数值
			if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
				//获取当前构造器函数
				Constructor<?> uniqueCandidate = candidates[0];
				//如果唯一的构造器函数的参数个数为0,则直接使用
				if (uniqueCandidate.getParameterCount() == 0) {
					synchronized (mbd.constructorArgumentLock) {
						//mbd中缓存解析好的构造器函数或工厂方法
						mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
						//mbd中标记构造器参数已解析
						mbd.constructorArgumentsResolved = true;
						//mbd中缓存解析好的构造器参数为EMPTY_ARGS
						mbd.resolvedConstructorArguments = EMPTY_ARGS;
					}
					//通过反射进行bean的实例化并返回
					bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
					return bw;
				}
			}

			// Need to resolve the constructor.
			// 1.如果传入的chosenCtors参数不为null
			// 2.autowireMode 选择的是构造器注入的方式(AUTOWIRE_CONSTRUCTOR)
			// 有以上两种情况之一:说明需要自动装配,设置autowiring为true
			boolean autowiring = (chosenCtors != null ||
					mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
			ConstructorArgumentValues resolvedValues = null;

			//构造器的最小参数个数
			int minNrOfArgs;
			//如果传入的explicitArgs不为null,则minNrOfArgs = explicitArgs.length
			if (explicitArgs != null) {
				minNrOfArgs = explicitArgs.length;
			}
			else {
				//获取配置文件中配置的构造器参数
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				//创建ConstructorArgumentValues对象,
				resolvedValues = new ConstructorArgumentValues();
				//解析参数个数
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}
			//将符合条件的构造器方法进行排序
			AutowireUtils.sortConstructors(candidates);
			// 定义一个差异变量,变量的大小决定着构造函数是否能够被使用
			int minTypeDiffWeight = Integer.MAX_VALUE;
			// 不明确的构造函数集合,正常情况下差异值不可能相同
			Set<Constructor<?>> ambiguousConstructors = null;
			//定义一个用于UnsatisfiedDependencyException的列表
			LinkedList<UnsatisfiedDependencyException> causes = null;

			//遍历获取到的构造方法
			for (Constructor<?> candidate : candidates) {
				//获取每个构造方法的参数个数
				int parameterCount = candidate.getParameterCount();
				// 1. 已经找到选用的构造函数
				// 2. 需要的参数个数小于当前的构造函数参数个数则终止,前面已经经过了排序操作
				if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
					// Already found greedy constructor that can be satisfied ->
					// do not look any further, there are only less greedy constructors left.
					break;
				}
				// 当前构造器函数所需参数数量 < 要求的参数数量,跳过,遍历下一个
				if (parameterCount < minNrOfArgs) {
					continue;
				}

				ArgumentsHolder argsHolder;
				//获取构造器所需参数类型数组
				Class<?>[] paramTypes = candidate.getParameterTypes();
				if (resolvedValues != null) {
					try {
						// 获取构造函数上的ConstructorProperties注解中的参数
						String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
						//如果没有上面注解,则获取构造器方法参数列表中的属性名称
						if (paramNames == null) {
							ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
							if (pnd != null) {
								//获取指定的构造器参数名称
								paramNames = pnd.getParameterNames(candidate);
							}
						}
						// 根据名称和数据类型创建argsHolder对象
						argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
								getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
					}
					catch (UnsatisfiedDependencyException ex) {
						// Swallow and try next constructor.
						if (causes == null) {
							causes = new LinkedList<>();
						}
						causes.add(ex);
						continue;
					}
				}
				//不存在构造函数参数列表需要解析的参数
				else {
					// Explicit arguments given -> arguments length must match exactly.
					// 如果参数列表的数量与传入进来的参数数量不相等,继续遍历,否则构造参数列表封装对象
					if (parameterCount != explicitArgs.length) {
						continue;
					}
					// 构造函数没有参数的情况
					argsHolder = new ArgumentsHolder(explicitArgs);
				}

				// 计算差异量,根据要参与构造函数的参数列表和本构造函数的参数列表进行计算
				// 省略这部分代码

			// 存在两种情况
			// 1. 没有确定使用的构造器函数
			// 2、存在模糊的构造函数并且不允许存在模糊的构造函数
			if (constructorToUse == null) {
				if (causes != null) {
					UnsatisfiedDependencyException ex = causes.removeLast();
					for (Exception cause : causes) {
						this.beanFactory.onSuppressedException(cause);
					}
					throw ex;
				}
				throw new BeanCreationException;
			}
			else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
				throw new BeanCreationException;
			}
			/**
			 * 没有传入参与构造函数参数列表的参数时,对构造函数缓存到BeanDefinition中
			 * 	1、缓存BeanDefinition进行实例化时使用的构造函数
			 * 	2、缓存BeanDefinition代表的Bean的构造函数已解析完标识
			 * 	3、缓存参与构造函数参数列表值的参数列表
			 */
			if (explicitArgs == null && argsHolderToUse != null) {
				argsHolderToUse.storeCache(mbd, constructorToUse);
			}
		}
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}

debug

具体流程
方法开始会创建BeanWrapper对象并初始化,初始化时会设置conversionService和注册BeanFactory中所有的Editors,目的是将值进行转换,因为我们在xml中配置的具体value都是字符串,比如说 age,而我们在具体类中对应的是Integer,在这里初始化后等到赋值时可以直接用。

protected void initBeanWrapper(BeanWrapper bw) {
		bw.setConversionService(getConversionService());
		registerCustomEditors(bw);
	}

声明构造器变量以及参数变量,因为我们此时对象还没有加载,所以缓存中constructorArgumentsResolvedargsToResolve等变量目前都为null,所以这些判断都进不去。
在这里插入图片描述


缓存中没有,所以我们需要获取当前实例中的所有构造器,并挑选一个符合条件的作为我们Bean的实例的容器。当前Person对象中我定义了一个无参构造器和一个包含name和age的构造器方法,所以这里candidates长度为2.
在这里插入图片描述


如果只获取到了一个构造器方法 && 没有构造器函数的参数值(mbd.hasConstructorArgumentValues()) && 也没有传入显示指定的参数(explicitArgs == null
则直接使用instantiate进行Bean的实例化,并设置缓存resolvedConstructorOrFactoryMethodconstructorArgumentsResolvedresolvedConstructorArguments
我们这里有两个构造器参数,所以进不来。
在这里插入图片描述


看是否需要进行自动装配
chosenCtors是方法的传参,这里是null,并且我们也没有在xml中配置 autowiring 属性,所以这里也进不来。
在这里插入图片描述


上面所有的条件都不满足,接下来就要根据我们获得的构造器方法列表和xml中配置的定义属性来挑选一个符合条件的构造器,作为对象实例化加载的容器。

获取到xml中配置的构造器参数。
在这里插入图片描述
解析参数个数,赋值给resolevedValues变量中。并将获取到的构造器进行排序操作。

排序:排序操作很重要,因为如果类中属性特别多,那么对应的生成的构造函数也可能会特别多,排序后再遍历会将不符合条件的直接continue,效率会大大提升。
在这里插入图片描述


遍历我们获取到的构造方法,因为做了排序,所以此时来到的是 name,age的构造方法。
如果已经在缓存中找到了构造函数 && 当前构造函数的参数个数(parameterCount) < 具体实例化时要用到的参数个数(argsToUse)。
说明没有再继续遍历的必要了,直接break。
如果当前构造方法的参数个数(parameterCount) < 我们需要实例化最小的参数个数 (minNrOfArgs),跳过这次循环 continue(当我们第二次循环condidates集合时,来到了无参构造,就会直接continue)
在这里插入图片描述


获取构造方法中的参数名称。
在这里插入图片描述


根据我们解析到xml中的值和当前构造方法的paramNames,创建并封装成argsHolder对象。
在这里插入图片描述


Spring中会计算每个构造器的权重,并通过逻辑选出最适合当前的构造器参数。
在这里插入图片描述


此时,将最终确认的构造器以及参数进行缓存。并调用instantiate方法进行bean的实例化创建并返回。
在这里插入图片描述

public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {
			synchronized (mbd.constructorArgumentLock) {
				mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;
				mbd.constructorArgumentsResolved = true;
				if (this.resolveNecessary) {
					mbd.preparedConstructorArguments = this.preparedArguments;
				}
				else {
					mbd.resolvedConstructorArguments = this.arguments;
				}
			}
		}

此时!我们的p1对象已经创建完成,当我们p2对象再次进行创建时。
缓存中不为null,设置我们的resolved 和 autowireNecessary变量为true。
在这里插入图片描述


再次调用autowireConstructor方法时。缓存不为null直接从缓存中找。找到后直接调用instantiate方法进行bean的实例化。(只有xml中配置 scope=“prototype” 才可以看到效果,不然创建完p1后直接放入 singletonObjects 缓存, 等到p2创建直接从singletonObjects中获取了)

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

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

相关文章

文章浮现之单细胞VDJ的柱状图

应各位老师的需求复现一篇文章的中的某个图 具体复现图5的整个思路图&#xff0c;这里没有原始数据&#xff0c;所以我使用虚拟生产的metadata进行画图 不废话直接上代码&#xff0c;先上python的代码的结果图 import matplotlib.pyplot as plt import numpy as np# 数据&#…

Linux 交叉编译工具链格式 sqlite3编译示例

1、交叉编译工具链 1.1 定义 交叉编译工具链是一个由编译器、连接器和解释器组成的综合开发工具集&#xff0c;它允许开发者在一个平台上&#xff08;例如高性能的PC或服务器&#xff09;编译生成另一个平台&#xff08;例如嵌入式系统或不同的操作系统和硬件架构&#xff09…

spring boot初始化的几个总结

spring intializr File->New->Project 注意&#xff1a;Spring Initializer中 Java版本选择模块已经不支持1.8了。 Spring Boot 3.x要求 Java最低版本为17&#xff0c; 最新的SpringBoot版本已经要求Java22了 所以&#xff0c;你可以升级Java版本&#xff0c;使用Spri…

自定义指令directive

一、在src目录下创建一个directive文件夹 test.ts文件存放创建的自定义指令&#xff0c;index.ts用于接收所有指令进行统一处理 二、编写自定义指令 // test.ts文件 export default {// 写个自定义指令mounted(el: any, binding: any) {console.log(el, binding, "&qu…

JVM相关总结

JVM的些许问题 1.JVM内存区域划分 2.JVM类加载过程 3.JVM的垃圾回收机制 1.JVM的内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机,需要从操作系统申请一大片内存,就会把内存划分成几个区域,每个区域都有不同的作用 常见的面试题 2.JVM类加载过程 熟练背诵 ! ! !…

Winform使用Flurl调用WebApi的基本用法

微信公众号“CSharp编程大全"的文章《.NET超简单轻量级的HTTP请求组件Flurl》介绍了便捷构建URL及创建HTTP请求的.NET模块Flurl。与HttpClient相比,Flurl封装的更简捷易用&#xff0c;代码量更少。本文学习并测试基于Fluri调用WebApi的基本用法。   基于Fluri调用WebApi…

怎么找python的运行路径

1.命令行中执行&#xff1a; import sys print(sys.argv[0]) 执行后为空。 2. import os os.path.abspath(os.curdir) 3. import os os.getcwd()

LeetCode-213. 打家劫舍 II【数组 动态规划】

LeetCode-213. 打家劫舍 II【数组 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;分三种情况&#xff0c;一&#xff1a;不考虑头尾&#xff1b;二&#xff1a;考虑头不考虑尾&#xff1b;三&#xff1a;考虑尾不考虑头。解题思路二&#xff1a;优化空间解题思路三&am…

如何利用python画出AHP-SWOT的战略四边形(四象限图)

在企业或产业发展的相关论文分析中&#xff0c;常用到AHP-SWOT法进行定量分析&#xff0c;形成判断矩阵后&#xff0c;如何构造整洁的战略四边形是分析的最后一个环节&#xff0c;本文现将相关代码发布如下&#xff1a; import mpl_toolkits.axisartist as axisartist import …

java之命令执行审计思路

1 漏洞原理 因用户输入未过滤或净化不完全&#xff0c;导致Web应用程序接收用户输入&#xff0c;拼接到要执行的系统命令中执行。一旦攻击者可以在目标服务器中执行任意系统命令&#xff0c;就意味着服务器已被非法控制。 2 审计中常用函数 一旦攻击者可以在目标服务器中执行…

【AIGC】AnimateAnyone:AI赋予静态照片生命力的魔法

摘要&#xff1a; 在人工智能技术的不断进步中&#xff0c;AnimateAnyone项目以其创新性和易用性脱颖而出&#xff0c;成为GitHub上备受瞩目的AI项目之一。由阿里巴巴智能计算研究院开发的这一技术&#xff0c;允许用户通过提供一张静态照片&#xff0c;快速生成动态角色。本文…

SpringBoot(一)创建一个简单的SpringBoot工程

Spring框架常用注解简单介绍 SpringMVC常用注解简单介绍 SpringBoot&#xff08;一&#xff09;创建一个简单的SpringBoot工程 SpringBoot&#xff08;二&#xff09;SpringBoot多环境配置 SpringBoot&#xff08;三&#xff09;SpringBoot整合MyBatis SpringBoot&#xff08;四…

docker安装rocketMq5x以上的版本

1.背景 安装RocketMQ 5.x以上的版本主要是因为新版本引入了许多性能优化、新功能以及对已有特性的增强&#xff0c;这些改进可以帮助提升消息队列系统的稳定性和效率。 1.性能提升&#xff1a;RocketMQ 5.x版本通常包括了对消息处理速度、吞吐量和延迟的优化&#xff0c;使得系…

在Linux (Ubuntu 16) 下安装LabVIEW

用户尝试在Ubuntu 16操作系统上安装LabVIEW&#xff0c;但找不到合适的安装文件来支持Ubuntu。已经下载了运行时文件&#xff0c;并尝试将.rpm包转换为.deb包并安装在Ubuntu上。然而&#xff0c;安装完成后&#xff0c;没有在应用程序中看到LabVIEW的图标。 用户希望能够在Ubu…

Spring MVC中的DispatcherServlet、HandlerMapping和ViewResolver的作用

在Spring MVC框架中&#xff0c;DispatcherServlet、HandlerMapping和ViewResolver是核心组件&#xff0c;它们各自承担着不同的角色和任务&#xff1a; 1.DispatcherServlet&#xff1a;它是Spring MVC生命周期中的前端控制器&#xff0c;负责接收HTTP请求并将它们分发给相应的…

【OpenREALM学习笔记:13】pose_estimation.cpp和pose_estimation.h

UML Class Diagram 图中红色框为头文件中所涉及到的函数、变量和结构体 核心函数 PoseEstimation::process() 其核心作用为执行位姿估计的处理流程&#xff0c;并返回是否在此循环中进行了任何处理。 在这个函数中判断并完成地理坐标的初始化或这地理坐标的更新。 这里需要…

论文阅读_基本于文本嵌入的信息提取

英文名&#xff1a;Embedding-based Retrieval with LLM for Effective Agriculture Information Extracting from Unstructured Data 中文名&#xff1a;基于嵌入的检索&#xff0c;LLM 从非结构化数据中提取有效的农业信息 地址: https://arxiv.org/abs/2308.03107 时间&…

昇思25天学习打卡营第04天|数据集 Dataset

数据是深度学习的基础&#xff0c;高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline的数据引擎&#xff0c;通过数据集&#xff08;Dataset&#xff09;和数据变换&#xff08;Transforms&#xff09;实现高效的数据预处理。其中Dataset是Pipel…

【linux】网络基础(1)

文章目录 网络基本概念网络的定义网络的类型局域网&#xff08;LAN&#xff09;广域网&#xff08;WAN&#xff09; 网络协议OSI七层模型TCP/IP模型TCP/IP模型的结构 网络传输的基本流程计算机与计算机之间的通信计算机的信息处理封装报头 网络基本概念 网络的定义 1.网络是指…

1.搭建篇——帝可得后台管理系统

目录 前言项目搭建一、搭建后端项目1.初始化项目Maven构建 2.MySQL相关导入sql配置信息 3. Redis相关启动配置信息 4.项目运行 二、 搭建前端项目1.初始化项目2.安装依赖3.项目运行 三、问题 前言 提示&#xff1a;本篇讲解 帝可得后台管理系统 项目搭建 项目搭建 一、搭建后…