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

上一篇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,则通过父容器获取beanif (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类型BeanDefinitionfinal 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,提前实例化需要依赖的Beanif (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册依赖的beanregisterDependentBean(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);}// 如果是原型beanelse 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 {// 创建其他类型作用域beanreturn 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转化成指定类型的beanT 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类型BeanDefinitionfinal 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,提前实例化需要依赖的Beanif (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册依赖的beanregisterDependentBean(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);}// 如果是原型beanelse 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 {// 创建其他类型作用域beanreturn 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.mzph.cn/news/868072.shtml

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

相关文章

Vue笔记11-Composition API的优势

Options API存在的问题 使用传统Options API中&#xff0c;新增或者修改一个需求&#xff0c;就需要分别在data&#xff0c;methods&#xff0c;computed里修改&#xff0c;而这些选项分布在代码的各个地方&#xff0c;中间还穿插着其他Optional API&#xff0c;如果代码量上来…

JAVA之(方法的重载与重写、this关键字、super关键字)

方法的重载与重写 一、方法的重载与重写1、回顾方法的定义2、重载的概念3、重写 二、this关键字1、何为this方法2、使用方法&#xff08;1&#xff09;在构造方法中指构造器所创建的新对象&#xff08;2&#xff09; 方法中指调用该方法的对象&#xff08;3&#xff09; 在类本…

centos7.9 rpm包安装mysql8.2.0数据库、root设置客户端登录、配置并发、表名大小写敏感、启动重启指令等记录

centos安装mysql8数据库,下载的是rpm-bundle.tar包,这样可以在内网环境离线安装,工作中医院的服务器很多也是内网的,所以这里记录下rpm-bundle.tar包安装的步骤。 lscpu 查看处理器是x86还是arm 下载对应的版本 bundle tar包 ((mysql-8.2.0-1.el7.x86_64.rpm-bundle.tar))…

2023年了,还在手动px转rem吗?

px-to-rem 使用amfe-flexible和postcss-pxtorem在webpack中配置px转rem npm i amfe-flexible -Snpm i postcss-pxtorem -D在main.js中 import flexible from amfe-flexible Vue.use(flexible);index.html中 <meta name"viewport" content"widthdevice-w…

Web3D引擎,three.js堪称扛把子,Babylon.js差点意思。

涉及到Web3D开发&#xff0c;Three.js和Babylon.js是两个备受推崇的引擎。它们都是基于WebGL的开源3D引擎&#xff0c;用于创建交互式的3D图形应用程序&#xff0c;但要细论起来&#xff0c;three.js普及度远超Babylon .js. 一、二者的介绍 Three.js&#xff1a; Three.js 是一…

Socket编程用到的函数TCP UDP实例

最基本的 Socket 模型 参考这次答应我&#xff0c;一举拿下 I/O 多路复用&#xff01; (qq.com) Socket编程详解-CSDN博客 Socket是一种通信机制&#xff0c;通过它可以在不同主机之间进行数据交换。在Socket编程中&#xff0c;有两种常见的通信模式&#xff1a;客户端-服务…

PIP换源的全面指南

##概述 在Python的世界里&#xff0c;pip是不可或缺的包管理工具&#xff0c;它帮助开发者安装和管理Python软件包。然而&#xff0c;由于网络条件或服务器位置等因素&#xff0c;直接使用默认的pip源有时会遇到下载速度慢或者连接不稳定的问题。这时&#xff0c;更换pip源到一…

在Linux操作系统中去修复/etc/fstab文件引起的系统故障。

如果/etcfstab文件中发生错误&#xff0c;有可能导致系统无法正常启动。 比如&#xff1a;系统里的一块磁盘被删除&#xff0c;但是/etc/fstab中关于这块磁盘的信息依然被保存在文件/etc/fstab中。 主要看倒数后两行&#xff0c;系统提示&#xff0c;敲ctrlD或者是直接输入密码…

Linux muduo 网络库

主要记录示意图和知识点框架&#xff1a; 1、阻塞、非阻塞、同步、异步 在处理IO的时候&#xff0c;阻塞和非阻塞都是同步IO&#xff0c;只有使用了特殊的API才是异步IO。 2、五种IO模型&#xff1a; 阻塞、非阻塞、IO复用、信号驱动、异步IO 3、muduo网络库 muduo网络库给用…

【Java】垃圾回收学习笔记(一):Root Search 根可达算法+垃圾回收的起点

文章目录 1. 引用计数法优点缺点 2. 可达性分析 Root Search2.1 那些对象是GC Roots2.2 引用的分类2.3 回收方法区 3. 实现细节3.1 GC的起点&#xff1a;节点枚举OopMap&#xff1a;帮助高效的根节点枚举 3.2 何时开始GC&#xff1a;安全点与安全区域如何选取安全点如何让程序进…

rocketmq-console可视化界面功能说明

rocketmq-console可视化界面功能说明 登录界面OPS(运维)Dashboard(驾驶舱)Cluster(集群)Topic(主题)Consumer(消费者)Producer(生产者)Message(消息)MessageTrace(消息轨迹) rocketmq-console是rocketmq的一款可视化工具&#xff0c;提供了mq的使用详情等功能。 本章针对于rock…

玫瑰千层烤饼:味蕾的芬芳盛宴

在美食的缤纷世界里&#xff0c;有一种独特的存在&#xff0c;它融合了玫瑰的芬芳与烤饼的酥脆&#xff0c;那便是令人陶醉的甘肃美食玫瑰千层烤饼。食家巷玫瑰千层烤饼&#xff0c;宛如一件精心雕琢的艺术品。每一层薄如纸张的面皮&#xff0c;都承载着制作者的细腻与用心。层…

【qt】TCP 服务端怎么收到信息?

上一节,我已经讲了,TCP的监听,是基于上一节的,不知道的可以看看. 当我们的TCP 服务器 有 客户端请求连接的时候,会发出一个信号newConnection(). 在TCP服务端与客户端的通信中,我们需要使用到套接字 QTcpSocket类. 套接字相当于是网络通信的接口,服务段和客户端都要通过它进行通…

Gitlab Fork Workflow(协作工作流)

Gitlab Fork WorkFlow&#xff08;协作工作流&#xff09; Fork WorkFlow用于团队间的协作开发。在开发过程中&#xff0c;我们都需要将最新修改的代码合并到代码库上&#xff0c;在代码合并之前&#xff0c;为了保证代码符合上传要求&#xff08;符合需求、代码规范等&#xf…

如何在应用运行时定期监控内存使用情况

如何在应用运行时定期监控内存使用情况 在 iOS 应用开发中&#xff0c;实时监控内存使用情况对于优化性能和排查内存泄漏等问题非常重要。本文将介绍如何在应用运行时定期监控内存使用情况&#xff0c;使用 Swift 编写代码并结合必要的工具和库。 1. 创建桥接头文件 首先&…

vue学习笔记之组件传值

说起组件传值&#xff0c;首先要介绍再vue中什么是组件。 组件&#xff08;Component&#xff09;&#xff0c;是vue中很强大的一个功能&#xff0c;可以将一些可重用的代码进行重用。所有的vue组件同时也是vue实例&#xff0c;可以接受使用相同的选项对象和提供相同的生命周期…

集合复习(java)

文章目录 Collection 接口Collection结构图Collection接口中的方法Iterator 与 Iterable 接口Collection集合遍历方式迭代器遍历增强 for 遍历 List&#xff08;线性表&#xff09;List特有方法ArrayList&#xff08;可变数组&#xff09;ArrayList 底层原理ArrayList 底层原理…

JAVA集合框架、CAS、AQS

目录 一、java 的集合框架有哪些? 二、说-下 ArrayList 和 LinkedList? 三、HashSet和TreeSet的区别? 四、HashMap 的数据结构是什么? 五、CAS机制 六、AQS理解 一、java 的集合框架有哪些? Collection 是 Java 集合框架中的一个根接口&#xff0c;位于 java.util 包中。它…

【SVN的使用-源代码管理工具-命令行的使用 Objective-C语言】

一、接下来,我们来说一个终端的命令行的使用, 1.我们说,你的电脑里边呢,有终端, 在Mac里边,你想新建一个txt,应该怎么写,对,打开文本编辑, 打开这个东西,写点儿东西,然后保存一下,保存的时候,你还要去选择格式, 现在,如果我们用命令行,可以更方便一些, 2.首…

【基于R语言群体遗传学】-10-适应性与正选择

在之前的博客中&#xff0c;我们学习了哈代温伯格模型&#xff0c;学习了Fisher模型&#xff0c;学习了遗传漂变与变异的模型&#xff0c;没有看过之前内容的朋友可以先看一下之前的文章&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 一些新名词 &#xff08;1&#xf…