Spring源码十七:Bean实例化入口探索

news2025/1/9 16:39:50

上一篇Spring源码十六:Bean名称转化我们讨论doGetBean的第一个方法transformedBeanName方法,了解Spring是如何处理特殊的beanName(带&符号前缀)与Spring的别名机制。今天我们继续往方法下面看:


doGetBean 

 这个方法比较长,之所以每次都放到这里也是方便大家回忆,一次两次肯定记不住的,不过具体到某个章节讨论的点,在代码后面的截图都会给大家标注出来的。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 获取转换后的Bean名称
		final String beanName = transformedBeanName(name);

		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		// 检查单例缓存中是否有手动注册的单例Bean
		// 这里是三级缓存处理方法入
		Object sharedInstance = getSingleton(beanName);
		// 如果单缓存中存在则直接从缓存中获取,
		// 如果不存在就走else分支,进行实例化
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			// 获取Bean的实例对象
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 如果当前bean还未被实例化,则在这个判断中准备实例化
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// 如果bean的类型是prototype且正在创建,直接抛出异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			// 检查Bean定义是否存在于当前工厂
			/// 获取容器的父容器
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// 存在父容器,且当前容器没有beanName的BeanDefinition,则通过父容器获取bean
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			if (!typeCheckOnly) {
				// 标记bean已经开始创建,其实就是将当前beanName 放入alreadyCreated容器中
				markBeanAsCreated(beanName);
			}

			// 进入实例化bean阶段
			// 1、处理BeanDefinition:合并同名的BeanDefinition、是否达到实例化标准
			// 2、处理依赖的Bean,如果存在依赖其他的bean则递归所有需要依赖的bean,提前实例化需要依赖的Bean
			// 3、开始创建Bean实例对象:根据作用域分类处理:
			// 		3.1 单例bean创建:创建单例bean的实例对象注册到容器中、获取容器中的对象
			//		3.2.原型bean创建:实例化前置准备工作、创建原型bean实例对象注册都容器中、原型bean实例化后置处理、获取容器中的对象
			// 		3.3 其他bean创建:实例化前置准备工作、创建其他类型bean实例对象注册都容器中、其他类型bean实例化后置处理、获取容器中的对象
			// 4、返回响应处理,根据入参转化bean的类型

			try {
				// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 获取当前beanName所依赖的BeanNames数组
				String[] dependsOn = mbd.getDependsOn();
				// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 注册依赖的bean
						registerDependentBean(dep, beanName);
						try {
							// 递归调用getBean,需要依赖的Bean先实例化
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				// 判断beanDefinition是单例?
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//是单例:则开始创建单例bean的实例
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 获取bean实例对象
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// 如果是原型bean
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						// 实例化前置准备工作
						beforePrototypeCreation(beanName);
						// 开始实例化
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						// 实例化以后的处理工作
						afterPrototypeCreation(beanName);
					}
					// 获取原型bean实例对象
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				// 其他bean类型处理
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					// 空抛出异常
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						// 处理流程类似原型
						Object scopedInstance = scope.get(beanName, () -> {
							// 创建其他类型作用域bean的前置准备工作
							beforePrototypeCreation(beanName);
							try {
								// 创建其他类型作用域bean
								return createBean(beanName, mbd, args);
							}
							finally {
								// 后置处理作用
								afterPrototypeCreation(beanName);
							}
						});
						// 获取其他作用域类型实例化对象
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		// 如果requiredType,且bean类型与入参要求不一致,则需要惊喜转换
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				// 将BeanDefinition转化成指定类型的bean
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

寻找实例化bean的入口

原型Bean校验

Spring中默认都是懒加载的单例bean,有因为单例bean全局唯一的特性,Spring为了提升性能避免频繁创建所以引入的缓存。将实例化过的bean放入缓存中。如下getSingleton方法就是判断单例缓存中是否存在同名的bean如果存在,则if逻辑。因为我们是第一次创建,缓存中自然没有我们的bean,所以进入else逻辑。

进入else逻辑,第一个处理方法如下,看到方法名称与注释大概也能这个方法是做了个基础校验,如果Bean的作用域是原型类型,且此时bean正在创建则抛出异常,说明

进入方法内部看下:

	protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}

方法isPrototypeCurrentlyInCreation中有一个集合prototypesCurrentlyInCreation,主要的逻辑就是判断下集合中是否存在beanName,prototype类型的bean实例每次来获取时都会创建一个新的bean实例,所以Spring也就没必要为这种类型的bean进行缓存;没有存放的缓存所以,一旦发现名称为beanName的bean正在创建,就会抛出异常来终止本次bean的创建。

当前容器没有beanName的BeanDefinition尝试去父容器获取

接着往下看:

有上述代码可知道:如果当前的Spring容器的父类容器是存在的,并且当前Spring容器中不存在beanName对应的BeanDefinition,就会到Spring的父类容器中获取bean的实例了。

进入originalBeanName方法看下:

	protected String originalBeanName(String name) {
		String beanName = transformedBeanName(name);
		if (name.startsWith(FACTORY_BEAN_PREFIX)) {
			beanName = FACTORY_BEAN_PREFIX + beanName;
		}
		return beanName;
	}

这个方法要注意下,他对name进行了处理,先通过transformedBeanName方法获取转换后的名称,然后在判断原始参数传入的name是否以&开头,如果是则在拼上去,至于为啥怎么做后面在看,这里心眼有个印象:知道主要是处理FactoryBean好。

咱们再接着往下看:

上述代码就比较简单,无非是根据入参情况,去父类容器获取bean。

可以看到默认传入的都是false,这里还是简单看下markBeanAsCreate方法:

	protected void markBeanAsCreated(String beanName) {
		if (!this.alreadyCreated.contains(beanName)) {
			synchronized (this.mergedBeanDefinitions) {
				if (!this.alreadyCreated.contains(beanName)) {
					// Let the bean definition get re-merged now that we're actually creating
					// the bean... just in case some of its metadata changed in the meantime.
					clearMergedBeanDefinition(beanName);
					this.alreadyCreated.add(beanName);
				}
			}
		}
	}

上述方法判断集合alreadyCreated中是否存在beanName,alreadyCreated是用来记录已经创建了的bean的名称。

上述方法有一个double check不知道小伙伴有没有注意到,这个在框架内部还是有很多地方有使用到的。作用嘛,当然这也是为了避免多线程并发的问题,方法中最为关键的,就是在集合alreadyCreated中添加beanName,并且清除了beanName对应BeanDefinition相关的信息。

可以发现到目前为止都是准备工作,真正的入口就在下面:咱们我们接着往下看

 Bean实例步骤

// 进入实例化bean阶段
			// 1、处理BeanDefinition:合并同名的BeanDefinition、是否达到实例化标准
			// 2、处理依赖的Bean,如果存在依赖其他的bean则递归所有需要依赖的bean,提前实例化需要依赖的Bean
			// 3、开始创建Bean实例对象:根据作用域分类处理:
			// 		3.1 单例bean创建:创建单例bean的实例对象注册到容器中、获取容器中的对象
			//		3.2.原型bean创建:实例化前置准备工作、创建原型bean实例对象注册都容器中、原型bean实例化后置处理、获取容器中的对象
			// 		3.3 其他bean创建:实例化前置准备工作、创建其他类型bean实例对象注册都容器中、其他类型bean实例化后置处理、获取容器中的对象
			// 4、返回响应处理,根据入参转化bean的类型

			try {
				// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 获取当前beanName所依赖的BeanNames数组
				String[] dependsOn = mbd.getDependsOn();
				// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 注册依赖的bean
						registerDependentBean(dep, beanName);
						try {
							// 递归调用getBean,需要依赖的Bean先实例化
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				// 判断beanDefinition是单例?
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//是单例:则开始创建单例bean的实例
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 获取bean实例对象
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// 如果是原型bean
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						// 实例化前置准备工作
						beforePrototypeCreation(beanName);
						// 开始实例化
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						// 实例化以后的处理工作
						afterPrototypeCreation(beanName);
					}
					// 获取原型bean实例对象
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				// 其他bean类型处理
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					// 空抛出异常
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						// 处理流程类似原型
						Object scopedInstance = scope.get(beanName, () -> {
							// 创建其他类型作用域bean的前置准备工作
							beforePrototypeCreation(beanName);
							try {
								// 创建其他类型作用域bean
								return createBean(beanName, mbd, args);
							}
							finally {
								// 后置处理作用
								afterPrototypeCreation(beanName);
							}
						});
						// 获取其他作用域类型实例化对象
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

在上述代码中备注已经把try代码块的处理逻辑给大家分析了,这里简单总结下:

  1. 合并BeanDefinition:首先,Spring容器会合并同名的BeanDefinition,确保每个Bean的配置是最新的,并且检查BeanDefinition是否满足实例化的条件。

  2. 检查依赖关系:如果Bean定义了依赖关系(通过depends-on属性),Spring容器会先注册并实例化这些依赖的Bean。如果检测到循环依赖,会抛出异常。

  3. 创建Bean实例:根据Bean的作用域(单例、原型或其他自定义作用域),Spring容器会采取不同的实例化策略:

    • 单例Bean:如果Bean是单例的,Spring容器会创建一个实例并将其注册到单例缓存中。
    • 原型Bean:如果Bean是原型的,每次请求都会创建一个新的实例。
    • 其他作用域Bean:对于其他自定义作用域的Bean,Spring容器会根据作用域的定义来创建和管理Bean的生命周期。
  4. 处理Bean的创建异常:如果在创建过程中发生异常,Spring容器会进行清理工作,并抛出异常。

  5. 获取Bean实例:最后,根据需要,Spring容器会返回Bean的实例或其代理。

小结

本篇咱们通过doGetBean方法一步一步进入,先是跳过了三级缓存获取单例bean方法,然后着重看了了原型Bean创建前的校验、父容器创建bean的场景,以及Spring容器在创建Bean实例时所遵循的步骤和逻辑。下一篇我们将探索Spring是如何来实例化bean。

总结

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

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

相关文章

机械键盘如何挑选

机械键盘的选择是一个关键的决策&#xff0c;因为它直接影响到我们每天的打字体验。在选择机械键盘时&#xff0c;有几个关键因素需要考虑。首先是键盘的键轴类型。常见的键轴类型包括蓝轴、红轴、茶轴和黑轴等。不同的键轴类型具有不同的触发力、触发点和声音。蓝轴通常具有明…

Partisia Blockchain 现已完成第一阶段空投,即将在DeFi领域发力

Partisia Blockchain 是以 MPC 方案为基础的 Layer1 生态&#xff0c;其具备可审计的隐私特性&#xff0c;同时还能保持链的可拓展、高迸发、可互操作以及安全等系列特性&#xff0c;Partisia Blockchain 被认为是目前最具潜力的企业级公链&#xff0c;并且估值高达 16 亿美元。…

身边的故事(十四):阿文的故事:再买房

短短的一年多时间里&#xff0c;阿文仿佛从人生低谷完全走出来了。各种眼花缭乱的操作和处理事情方式让人觉得不可思议&#xff0c;是不是一个人大手大脚花钱惯了&#xff0c;让他重新回到艰苦朴素的日子是不是比死都难受呢&#xff1f;又或者像我这种靠勤勤恳恳的打工人是无法…

博客搭建-图床篇

我们的博客难免少不了图片&#xff0c;图片管理是一个不小的难题。如果我们将图片全部放到我们自己的服务器上&#xff0c;那么带宽就基本上会被图片所占满了&#xff0c;这会导致网站加载很慢&#xff08;特别是图片加载很慢&#xff09;。 ‍ 什么是图床 为了解决图片的问…

ansible常见问题配置好了密码还是报错

| FAILED! > { “msg”: “Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host’s fingerprint to your known_hosts file to manage this host.” } 怎么解决&#xf…

计算两种人像之间的相似度

通过调研&#xff0c;目前存在几种能够计算两个人脸相似度的方法&#xff1a; 1.使用结构相似性计算人脸之间的相似度 结构准确性&#xff1a;生成的图片是否保留了原图足够多细节。 &#xff08;1&#xff09;结构准确性衡量指标&#xff1a;SSIM/MMSSIM SSIM&#xff08;结构…

昇思MindSpore学习笔记5-01生成式--LSTM+CRF序列标注

摘要&#xff1a; 记录昇思MindSpore AI框架使用LSTMCRF模型分词标注的步骤和方法。包括环境准备、score计算、Normalizer计算、Viterbi算法、CRF组合,以及改进的双向LSTMCRF模型。 一、概念 1.序列标注 标注标签输入序列中的每个Token 用于抽取文本信息 分词(Word Segment…

3-5 提高模型效果:归一化

3-5 提高模型效果&#xff1a;归一化 主目录点这里 举例 1. 批量归一化 (Batch Normalization, BN) 应用场景: 通常用于图像分类任务&#xff0c;它在训练期间对每个批次的数据进行归一化&#xff0c;以加速收敛并稳定训练过程。 代码示例: import torch import torch.…

【实践分享】深度学习远程连接GPU

目录 前言 一、创建实例 二、上传文件 三、服务器上传 四、运行代码文件 前言 1、使用平台&#xff1a;恒源云 2、教程总结自B站大佬Larry同学发布的教程视频 一、创建实例 通俗&#xff1a;租用一台临时的电脑&#xff0c;电脑可自选GPU型号等&#xff0c;按照项目需…

Linux基础:一. 简单的命令

文章目录 一. 简单的命令1.1 关机1.2 重启1.3 控制台打印工作目录1.4 切换当前目录1.5 列出当前目录中的目录和文件1.6 列出指定目录中的目录和文件1.7 控制台清屏1.8 查看和设置时间1.8.1 查看时间1.8.2 设置时间&#xff0c;需要管理员权限 一. 简单的命令 1.1 关机 comman…

FairJob:促进在线广告系统公平性研究

在人工智能&#xff08;AI&#xff09;与人类动态的交汇处&#xff0c;既存在机遇也存在挑战&#xff0c;特别是在人工智能领域。尽管取得了进步&#xff0c;但根植于历史不平等中的持续偏见仍然渗透在我们的数据驱动系统中&#xff0c;这些偏见不仅延续了不公平现象&#xff0…

PingCAP 成为全球数据库管理系统市场增速最快的厂商

近日&#xff0c;Gartner 发布的《Market Share Analysis: Database Management Systems, Worldwide, 2023》&#xff08;2024 年 6 月&#xff09;报告显示&#xff1a;“2023 年全球数据库管理系统&#xff08;DBMS&#xff09;市场的增长率为 13.4%&#xff0c;略低于去年的…

排序 -- 计数排序以及对排序的总结

到了这篇文章就说明常见的排序我们就快要讲完了&#xff0c;那这篇文章我们就讲一下非比较排序--计数排序。 一、非比较排序 1.基本思想 计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。 操作步骤&#xff1a; 统计相同元素出现次数 根据统计的结果将序列…

LaTeX教程(014)-LaTeX文档结构(14)

LaTeX教程(014)- LaTeX \LaTeX LATE​X文档结构(14) 2.3.3 multitoc - 将目录设置为多栏 multitoc包的使用方法相当简单&#xff0c;只需要调用这个包&#xff0c;并将要设置为多栏(默认是双栏)的目录指定到包选项中即可。如\usepackage[toc]{multitoc}&#xff0c;设置的就是…

25_嵌入式系统总线接口

目录 串行接口基本原理 串行通信 串行数据传送模式 串行通信方式 RS-232串行接口 RS-422串行接口 RS-485串行接口 RS串行总线总结 RapidIO高速串行总线 ARINC429总线 并行接口基本原理 并行通信 IEEE488总线 SCSI总线 MXI总线 PCI接口基本原理 PCI总线原理 PC…

jmeter-beanshell学习4-beanshell截取字符串

再写个简单点的东西&#xff0c;截取字符串&#xff0c;参数化文件统一用csv&#xff0c;然后还要用excel打开&#xff0c;如果是数字很容易格式就乱了。有同事是用双引号把数字引起来&#xff0c;报文里就不用加引号了&#xff0c;但是这样beanshell处理起来&#xff0c;好像容…

MATLAB中的SDPT3、LMILab、SeDuMi工具箱

MATLAB中的SDPT3、LMILab、SeDuMi工具箱都是用于解决特定数学优化问题的工具箱&#xff0c;它们在控制系统设计、机器学习、信号处理等领域有广泛的应用。以下是对这三个工具箱的详细介绍&#xff1a; 1. SDPT3工具箱 简介&#xff1a; SDPT3&#xff08;Semidefinite Progra…

Nacos服务注册总流程(源码分析)

文章目录 服务注册NacosClient找看源码入口NacosClient服务注册源码NacosServer处理服务注册 服务注册 服务注册 在线流程图 NacosClient找看源码入口 我们启动一个微服务&#xff0c;引入nacos客户端的依赖 <dependency><groupId>com.alibaba.cloud</groupI…

Science Robotics 麻省理工学院最新研究,从仿真中学习的精确选择、定位和抓放物体的视触觉方法

现有的机器人系统在通用性和精确性两个性能目标上难以同时兼顾&#xff0c;往往会陷入一个机器人解决单个任务的情况&#xff0c;缺乏"精确泛化"。本文针对精准和通用的同时兼顾提出了解决方法。提出了SimPLE(Pick Localize和placE的仿真模拟)作为精确拾取和放置的解…