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

news2024/11/23 19:03:57

上一篇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/1904724.html

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

相关文章

zabbix 与 grafana 对接

一.安装 grafana 1.初始化操作 初始化操作 systemctl disable --now firewalld setenforce 0 vim /etc/selinux/config SELINUXdisabled 2.上传数据包并安装 cd /opt grafana-enterprise-9.4.7-1.x86_64.rpm #上传软件包 yum localinstall -y grafana-enterprise-9.4.7-1…

Ubuntu编译 OSG

目录 一、安装步骤 二、配置 1、数据文件配置 2、OSG环境变量配置 一、安装步骤 在Ubuntu上安装OSG(OpenSceneGraph),你可以按照以下步骤操作: 打开终端,更新你的包管理器的包列表: sudo apt update 安装必要的依赖库 sudo apt install libglu1-mesa-dev freeglu…

java IO流(1)

一. 文件类 java中提供了一个File类来表示一个文件或目录(文件夹),并提供了一些方法可以操作该文件 1. 文件类的常用方法 File(String pathname)构造方法,里面传一个路径名,用来表示一个文件boolean canRead()判断文件是否是可读文件boolean canWrite()判断文件是否是可写文…

18_特征金字塔网络FPN结构详解

1.1 简介 在深度学习领域&#xff0c;尤其是计算机视觉和目标检测任务中&#xff0c;Feature Pyramid Networks (FPN) 是一种革命性的架构设计&#xff0c;它解决了多尺度特征检测和融合的关键问题。FPN最初由何凯明等人在2017年的论文《Feature Pyramid Networks for Object …

阿里云ecs服务器,nginx多域名多项目部署教程,含本地部署教程

nginx多域名部署项目 本地部署线上部署一、本地部署 第一步:win+r 输入drivers 打开hosts文件,编辑 加行 127.0.0.1 自定义域名 … 第二步:下载 nginx 安装好以后 打开ngin安装目录,选择nginx.conf 打开 #user Administrator; worker_processes

【python技巧】parser传入参数

参考网址: https://lightning.ai/docs/pytorch/LTS/api/pytorch_lightning.utilities.argparse.html#pytorch_lightning.utilities.argparse.add_argparse_args 1. 简单传入参数. parse_known_args()方法的作用就是把不在预设属性里的参数也返回,比如下面这个例子, 执行pytho…

前端技术(三)—— javasctipt 介绍:jQuery方法和点击事件介绍(补充)

6. 常用方法 ● addClass() 为jQuery对象添加一个或多个class <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

【密码学】什么是密码?什么是密码学?

一、密码的定义 根据《中华人民共和国密码法》对密码的定义如下&#xff1a; 密码是指采用特定变换的方法对信息等进行加密保护、安全认证的技术、产品和服务。 二、密码学的定义 密码学是研究编制密码和破译密码的技术科学。由定义可以知道密码学分为两个主要分支&#x…

【UML用户指南】-30-对体系结构建模-模式和框架

目录 1、机制 2、框架 3、常用建模技术 3.1、对设计模式建模 3.2、对体系结构模式建模 用模式来详述形成系统体系结构的机制和框架。通过清晰地标识模式的槽、标签、按钮和刻度盘 在UML中&#xff0c; 对设计模式&#xff08;也叫做机制&#xff09;建模&#xff0c;将它…

前端必修技能:高手进阶核心知识分享 - CSS 阴影属性详解

CSS 涉及设计到阴影的相关内容包括三个方面&#xff1a;box-shadow属性&#xff08;盒子阴影&#xff09;、 text-shadow属性&#xff08;文本阴影&#xff09;、drop-shadow滤镜。 本篇文章旨在详细介绍和分析三种阴影的具体参数设置和典型用例。 box-shadow属性&#xff08;…

1958.力扣每日一题7/7 Java(100%解)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 思路 解题方法 时间复杂度 空间复杂度 Code 思路 首先将指定位…

【ubuntu中关于驱动得问题】—— 如何将nouveau驱动程序加入黑名单和安装NVIDIA显卡驱动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、nouveau驱动程序加入黑名单二、安装NVIDIA显卡驱动总结 前言 NVIDIA显卡驱动是用于支持和优化NVIDIA显卡在计算机系统中运行的关键软件组件。该驱动程序能…

2024 WAIC|第四范式胡时伟分享通往AGI之路:行业大模型汇聚成海

7月4日&#xff0c;2024世界人工智能大会&#xff08;WAIC&#xff09;正式开幕。此次大会围绕核心技术、智能终端、应用赋能等板块展开&#xff0c;展览规模、参展企业数均达历史最高。第四范式受邀参展&#xff0c;集中展示公司十年来在行业大模型产业应用方面的实践。在当天…

Django 查询数据

模型参考上一章内容&#xff1a; Django QuerySet对象&#xff0c;filter()方法-CSDN博客 查询数据可以通过以下方法&#xff1a; Book.objects.all() Book.objects.filter() Book.objects.get() 1&#xff0c;添加视图函数 Test/app11/views.py from django.shortcuts im…

【QT中堆栈布局的实现】

学习分享 1、环境设置&#xff0c;头文件2、.h文件2.1、主界面.h文件2.2、对话界面1.h文件2.3、对话界面2.h文件 3、.cpp文件3.1、对话界面1.cpp3.2、对话界面2.cpp3.3、主界面.cpp3.4、main.cpp 1、环境设置&#xff0c;头文件 该示例使用C14实现&#xff0c;因此在QT项目pro文…

实战:搭建一款属于自己的个人知识库~docusaurus(强大且丝滑)-2024.7.7(测试成功)

目录 文章目录 目录docusaurus简介效果专题链接&#x1f44f;环境源码1、安装基础环境2、拉取代码3、安装坚果云并同步md核心文件4、构建运行5、配置脚本环境1.配置vscode终端到ecs的免密2.配置win10 vscode终端环境变量 6、构建并推送静态文件到ecs关于我最后最后 docusaurus简…

基于CentOS Stream 9平台搭建RabbitMQ3.13.4以及开机自启

1. erlang与RabbitMQ对应版本参考&#xff1a;https://www.rabbitmq.com/which-erlang.html 2. 安装erlang 官网&#xff1a;https://www.erlang.org/downloads GitHub: https://github.com/rabbitmq/erlang-rpm/releases 2.1 安装依赖&#xff1a; yum -y install gcc glib…

BigDecimal(double)和BigDecimal(String)有什么区别?BigDecimal如何精确计数?

BigDecimal(double)和BigDecimal(String)的区别 double是不精确的&#xff0c;所以使用一个不精确的数字来创建BigDecimal&#xff0c;得到的数字也是不精确的。如0.1这个数字&#xff0c;double只能表示他的近似值。所以&#xff0c;当我们使用new BigDecimal(0.1)创建一个Bi…

算法简介:什么是算法?——定义、历史与应用详解

引言 在现代计算机科学中&#xff0c;算法是一个核心概念。无论是编程还是数据分析&#xff0c;算法都扮演着至关重要的角色。在这篇博客中&#xff0c;我们将深入探讨算法的定义、历史背景以及它在计算机科学中的地位和实际应用。 什么是算法&#xff1f; 算法是解决特定问题…

STM32基础篇:GPIO

GPIO简介 GPIO&#xff1a;即General Purpose Input/Output&#xff0c;通用目的输入/输出。就是一种片上外设&#xff08;内部模块&#xff09;。 对于STM32的芯片来说&#xff0c;周围有一圈引脚&#xff0c;有时需要对引脚进行读写&#xff08;读&#xff1a;从外部输入一…