spring源码篇(3)——bean的加载和创建

news2024/12/26 23:39:12

spring-framework 版本:v5.3.19

文章目录

  • bean的加载
  • bean的创建
  • 总结
    • getBean流程
    • createBean流程
      • doCreateBean流程

bean的加载

beanFactory的genBean最常用的一个实现就是AbstractBeanFactory.getBean()
以ApplicationContext为例,流程是: ApplicationContext.getBean()----》BeanFactory.getBean()----》AbstractBeanFactory.getBean()
在这里插入图片描述
而真正做事情的是deGetBean方法

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

		//获取beanName
		String beanName = transformedBeanName(name);
		Object beanInstance;

		//无论是什么作用域都先尝试从单例池获取
		Object sharedInstance = getSingleton(beanName);
		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 + "'");
				}
			}
			//有时候并不是放回实例本身,而是返回其映射的实例。如:BeanFactory情况下返回指定方法返回的实例
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			//当原型模式下有循环依赖时,抛异常(原型模式下若存在循环依赖会导致内存泄漏)
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			//尝试从父beanFactory加载bean
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			//如果不是仅仅做类型检查则是创建bean,这里进行记录
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			try {
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
				//得到合并后的beanDefinition
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				//检查beanDefinition,抽象的beanDefinition会报错
				checkMergedBeanDefinition(mbd, beanName, args);

				//先加载dependsOn标注的bean
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						//dePendsOn引起的循环依赖,抛异常
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						//注册dependentBeanMap(dependsOn 于哪些bean)和注册dependenciesForBeanMap(被哪些bean dependOn)
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				//-----------------------------------开始创建bean-----------------------------------------------
				//单例模式
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//原型模式
				else if (mbd.isPrototype()) {
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				//其他模式
				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
					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, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}
		}
		//必要时进行类型转换
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

bean的创建

从bean的加载中得知,当一个从缓存中获取不到的时候就会调用createBean方法去创建一个bean。而在AbstractBeanFactory中这是一个抽象方法,具体的创建逻辑由具体的beanFactory类自行实现。至少目前为止,spring内置的实现只有AbstractAutowireCapableBeanFactory.createBean()。无论是AnnotationConfigApplicationContext还是ClassPathXmlApplicationContext创建bean的逻辑最终都会来到这个方法。

虽然AnnotationConfigApplicationContext和ClassPathXmlApplicationContext并不直接继承AbstractAutowireCapableBeanFactory。但是都维护了一个DefaultListableBeanFactory成员变量,而这个成员变量继承自AbstractAutowireCapableBeanFactory。

createBean
在这里插入图片描述

doCreateBean

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

		//如果是单例,factoryBeanCache.remove
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}

		//实例化bean
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		//MergedBeanDefinitionPostProcessor的应用(如:AutowiredAnnotationBeanPostProcessor)
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		//提前暴露bean,循环依赖处理
		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");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		Object exposedObject = bean;
		try {
			//属性填充
			//填充属性之前会调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
			//填充属性之后会调用 InstantiationAwareBeanPostProcessor.postProcessProperties --如果返回null--> postProcessPropertyValues
			populateBean(beanName, mbd, instanceWrapper);
			//初始化bean(是指据配置自定义的初始化bean如:@PostConstruct,而不是指构造方法的初始化)
			//初始化之前会调用 PostProcessors.postProcessBeforeInitialization
			//初始化之后会调用 PostProcessors.postProcessAfterInstantiation
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		//循环依赖检查,判断是否需要抛出异常
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		//注册DisposableBean,如果配置了destroy-methoy,这里需要注册以便于在销毁的时候调用
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

initializeBean
在这里插入图片描述


总结

getBean流程

1:transformedBeanName(name) 转换为对应的beanName
2:getSingleton(beanName) 尝试从缓存中加载单例
3:isPrototypeCurrentlyIncreation(beanName) 原型模式的依赖检查
4:检测父BeanFactory
5:getMergedLocalBeanDefinition(beanName) GernericBeanDefinition转换为RootBeanDefinition
6:依赖检测
7:根据不同的模式创建bean
8:getObjectForBeanInstance(prototypeInstance, name, beanName, mbd) 根据得到的Bean获取真正对象
9:如果需要则做类型转换

createBean流程

1:锁定class
2:指定方法的覆盖
3:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
4:doCreateBean

doCreateBean流程

1:若为单例,移除factoryBeanCache内的缓存
2:实例化Bean
3:MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(如:AutowiredAnnotationBeanPostProcessor)
4:提前暴露bean,循环依赖处理

(populateBean)
5:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
6:属性填充
7:InstantiationAwareBeanPostProcessor.postProcessProperties --如果返回null–> postProcessPropertyValues

(initializeBean)
8:aware方法的执行(beanNameAware,beanClassLoaderAware,beanFactoryAware)
9:PostProcessors.postProcessBeforeInitialization
10:自定义初始化bean(如:InitializingBean.afterPropertiesSet,@PostConstruct,init-method)
11:PostProcessors.postProcessAfterInstantiation

12:循环依赖检查,判断是否需要抛出异常
13:注册DisposableBean

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

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

相关文章

01 C语言计算

C语言计算 1、变量 用途&#xff1a;需要存放输入的数据 定义格式&#xff1a;数据类型 变量名&#xff08;用于区分其他变量&#xff09; 变量名格式&#xff1a;只能由字母/下划线/数字构成&#xff0c;首位不能是数字&#xff1b;且变量名不能是标识符 **变量赋值和初始…

Python每日一练(20230305)

目录 1. 正则表达式匹配 ★★★ 2. 寻找旋转排序数组中的最小值 II ★★★ 3. 删除排序链表中的重复元素 II ★★ 1. 正则表达式匹配 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符* 匹配零个或多个…

格式化字符串之在栈上修改got表,执行system(“/bin/sh“)

题目自取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1sZyC-d47cnrjQ0rmRNLbSg?pwdiung 提取码&#xff1a;iung 这是一题改got表的格式化字符串的例题 这里介绍下pwntools里的一个脚本 fmtstr_payload&#xff1a; 举个例子&#xff0c; payload fmtstr_payl…

谷歌浏览器被hao123网页(或其他网页)劫持了,怎么办?(已解决)

文章目录1、前言2、解决方案2.1、方案一&#xff1a;删除目标内容2.2、方案二&#xff1a;修改浏览器启动时内容2.3、方案三&#xff1a;重命名2.4、方案四&#xff1a;修改WMI脚本2.5、方案五&#xff1a;火绒修复3、总结1、前言 今天打开chrome浏览器&#xff0c;莫名转到hao…

【CMU15-445数据库】bustub Project #2:B+ Tree(下)

Project 2 最后一篇&#xff0c;讲解 B 树并发控制的实现。说实话一开始博主以为这块内容不会很难&#xff08;毕竟有 Project 1 一把大锁摆烂秒过的历史x&#xff09;&#xff0c;但实现起来才发现不用一把大锁真的极其痛苦&#xff0c;折腾了一周多才弄完。 本文分基础版算法…

【uni-app教程】八、UniAPP Vuex 状态管理

八、UniAPP Vuex 状态管理 概念 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 应用场景 Vue多个组件之间需要共享数据或状态。 关键规则 State&#xff1a…

Linux系统配置nginx

下载安装包wget -c http://nginx.org/download/nginx-1.19.1.tar.gz安装gcc安装包yum install gcc-c安装pre-devel依赖库yum -y install pcre-devel安装openssl依赖库yum -y install openssl openssl-devel解压tar -zxvf 目录名 nginx-1.23.1.tar.gz -C 另外一个目录&#xff0…

QT配置安卓环境(保姆级教程)

目录 下载环境资源 JDK1.8 NDK SDK ​安装QT 配置环境 下载环境资源 JDK1.8 介绍JDK是Java开发的核心工具&#xff0c;为Java开发者提供了一套完整的开发环境&#xff0c;包括开发工具、类库和API等&#xff0c;使得开发者可以高效地编写、测试和运行Java应用程序。 下载…

基于Vue3和element-plus实现一个完整的登录功能

先看一下最终要实现的效果:登录页面:注册页面:(1)引入element-plus组件库引入组件库的方式有好多种,在这里我就在main.js全局引入了.npm i element-plus -Smain.js中代码:import { createApp } from "vue"; //element-plus import ElementPlus from "element-pl…

Linux基础篇(七)-- 用户管理

1 创建普通用户 1、创建用户 在 Linux 系统里&#xff0c;root 账户&#xff08;超级管理员&#xff09;拥有整个系统至高无上的权限&#xff0c;比如新建和添加用户。一般我们登录系统时都是以普通账户的身份登录的&#xff0c;要创建用户需要 root 权限&#xff0c;…

项目:手把手实现高并发内存池

一.前言&#xff08;一&#xff09;.项目简介高并发内存池&#xff08;ConCurrentMemoryPool&#xff09;&#xff0c;其原型是google的开源项目tcmalloc。全称是thread-cache-malloc&#xff0c;即线程缓存malloc。应用场景是多线程环境下管理内存&#xff0c;相较于malloc库函…

Mysql数据库的(超详细)安装及环境变量的配置

一、 下载MySQL Mysql官网下载地址&#xff1a;https://downloads.mysql.com/archives/installer/ 1. 选择需要的版本点击Download进行下载 本篇文章选择的8.0.26版本 二、 安装MySQL 1. 选择设置类型 双击运行mysql-installer-community-8.0.26.msi&#xff0c;这里选择是…

GoldenGate(OGG)高可用XAG介绍

XAG介绍: Oracle Grid Infrastructure提供了高可用组件去管理实现集群上面服务的高可用&#xff0c;Oracle Grid Infrastructure agent&#xff08;XAG&#xff09;是Oracle Grid Infrastructure的一个管理组件&#xff0c;通过接口AGCTL在Oracle RAC集群上为应用程序(GoldenG…

【14】linux命令每日分享——userdel删除账号

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…

Visual Studio 高级调试-企业版三大特性

前言前面两篇博客主要介绍了Visual Studio 高级调试-代码调试和Visual Studio 高级调试-Dump分析&#xff0c;这几篇博客的目的都是为了帮助大家更好的认识和使用Visual Studio&#xff0c;Visual Studio企业版订阅价格为每月250美元&#xff0c;很多同学想知道企业版有哪些特别…

IsADirectoryError: [Errno 21] Is a directory: ‘.‘

项目场景&#xff1a; 基于YOLOv5的室内场景识别 工具&#xff1a;colab 问题描述 Traceback (most recent call last): File “train.py”, line 630, in main(opt) File “train.py”, line 494, in main d torch.load(last, map_location‘cpu’)[‘opt’] File “/usr/…

docker(三)仓库的搭建、官方私有仓库的加密和认证

文章目录一、docker仓库二、仓库Registry工作原理三、搭建本地私有仓库四、配置镜像加速器五、私有仓库的加密认证1.非加密下上传拉取2.insecure registry3.仓库加密4.仓库认证一、docker仓库 什么是仓库 Docker 仓库是用来包含镜像的位置&#xff0c;Docker提供一个注册服务器…

QML集成JavaScript

在QML中可以使用现有的QML元素来创建页面&#xff0c;但QML紧密的集成了必要的JavaScript。 但QML中使用JavaScript比较严格&#xff0c;在QML中不可以添加或修改JavaScript全局对象成员&#xff0c;这样可能会使用一个未经声明的变量。 内联JavaScript 一些小型的JavaScript函…

动态规划 背包问题

动态规划 背包问题 问题描述&#xff1a; 有一个背包&#xff0c;总容量为12。有6件物品&#xff0c;每件物品的重量和价值不同&#xff0c;求在背包总容量12的前提下&#xff0c;装进物品的最大价值以及装进物品的编号 单个物品重量和价值&#xff1a; 为方便进行思考&#…

06、Eclipse 中使用 SVN

Eclipse 中使用 SVN1 在 Eclipse 中安装 SVN 客户端插件1.1 在线安装1.2 离线安装2 SVN 在 Eclipse 分享3 检出提交更新3.1 检出3.2 提交3.3 更新4 Eclipse 中 SVN 图标及其含义4.1 &#xff1f;图标4.2 图标4.3 金色圆柱图标4.4 * 图标5 恢复历史版本5.1 恢复步骤5.2 权限控制…