002-Spring Bean 从扫描到创建

news2024/11/16 6:04:18

目录

  • Spring Bean 从扫描到创建
    • 扫描流程 class 到 BeanDefinition
    • 引入
    • 扫描:ClassPathBeanDefinitionScanner
      • doScan
      • findCandidateComponents(basePackage)
      • registerDefaultFilters()
      • this.registry
    • 创建流程 BeanDefinition 到 Bean
      • 统一 BeanDefinition 类型
        • finishBeanFactoryInitialization(beanFactory);
        • getMergedLocalBeanDefinition(beanName)
      • 创建Bean
        • doGetBean
        • 转化BeanName(transformedBeanName(name))
      • createBean(beanName, mbd, args)
        • resolveBeanClass(mbd, beanName)
        • createBeanInstance(beanName, mbd, args)
        • populateBean(beanName, mbd, instanceWrapper)
        • initializeBean(beanName, exposedObject, mbd)

Spring Bean 从扫描到创建

扫描流程 class 到 BeanDefinition

引入

上一个分析文章中我们知道从Class到一个Bean中间是需要把Class先创建成BeanDefinition
然后由Spring控制在不同的时机用不用的方法依据BeanDefinition转化为Bean供我们使用

那么Class到BeanDefinition的流程是什么?
先说结论这个过程主要发生在
ClassPathBeanDefinitionScanner : 扫描读取Class文件为Resource对象

扫描:ClassPathBeanDefinitionScanner

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
这个方法就是扫描包中的文件,把文件加载成Resource对象的并注册成为BeanDefinition的

doScan

分析下细节:

遍历basePackages : basePackage {
	查找到所有符合要求的BeanDefinition(findCandidateComponents(basePackage)) : candidates
	遍历所有的 BeanDefinitions : beanDefinitions {
		解析:Scope.class
		获取beanName
		if instanceof AbstractBeanDefinition : 执行postProcessBeanDefinition:
			1. 设置默认值
				setLazyInit
				setAutowireMode(defaults.getAutowireMode());
				setDependencyCheck(defaults.getDependencyCheck());
				setInitMethodName(defaults.getInitMethodName());
				setEnforceInitMethod(false);
				setDestroyMethodName(defaults.getDestroyMethodName());
				setEnforceDestroyMethod(false);
			2. setAutowireCandidate
		if instanceof AnnotatedBeanDefinition : 解析 @Lazy@Primary@DependsOn@Role@Description
		if (this.registry是否存在beanName) {
			注册BeanDefinitionthis.registry
		}
	}
}

findCandidateComponents(basePackage)

if 没有 编写索引文件 那么进入 scanCandidateComponents(basePackage); 

scanCandidateComponents(basePackage) {
	路径转化 包名转化为三部分组成的路径
		1. classpath*:
		2. 包名中的 "." 替换为 "/"
		3. /**/*.class
	扫描路径中的class文件为 Resource[] resources
	遍历 resources :: resource {
		判断是否符合条件
			1. excludeFilters 符合任意一个则排除(默认为空)
			2. includeFilters 符合任意一个则包含(默认registerDefaultFilters()3. 如果命中include 继续检查是否使用@Conditional
		if (没排除,且包含) {
			组装ScannedGenericBeanDefinition sbd
				1. this.beanClass = beanClassName;
				2. this.resource = resource;
				3. this.source = source;
			判断是否可以创建Bean
				1. 非内部类的Class
				2. &&(是具体类 || 是抽象类但是方法上有@Lookupif (判断通过) {
				添加到候选池中 candidates
			}
		}
	}
}

在这里插入图片描述

registerDefaultFilters()

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters
添加了三个includeFilter

  1. org.springframework.stereotype.Component
  2. javax.annotation.ManagedBean
  3. javax.inject.Named

this.registry

这里的 registry 并不是 AnnotationConfigApplicationContext 虽然 AppContext本身也是Registry
但是这里的 registry 是
org.springframework.context.annotation.ComponentScanAnnotationParser#parse 中 new 出来的
ClassPathBeanDefinitionScanner 中带有的 org.springframework.beans.factory.support.DefaultListableBeanFactory
而在 DefaultListableBeanFactory 维护了一个 Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);

  • 是否存在 : this.beanDefinitionMap.containsKey(beanName);
  • 注册:this.beanDefinitionMap.put(beanName, beanDefinition);

创建流程 BeanDefinition 到 Bean

统一 BeanDefinition 类型

finishBeanFactoryInitialization(beanFactory);

统一为:RootBeanDefinition

创建非懒加载单例bean
扫描所有的BeanDefinition创建Bean在:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

遍历beanDefinitionNames : beanNames : beanName {
	根据beanName 组装 和合并BenaDefinition(RootBeanDefinition) : bd
	//注意这里判断的是是不是抽象的BeanDefinition 而不是判断的是不是一个抽象类
	//抽象的BeanDefinition 在自定义BeanDefinition的时候可以提供一个些公共的属性
	if (bd 不是抽象的 && 是单例的 && 是懒加载的) {
		if (是工厂Bean(FactoryBean)) {
			//这里已经创建一个beanName的Bean了 创建的对象是Class对应的对象
			//也就是说在容器创建的是否并没有创建getObject指定的对象
			//getObject指定的对象是在使用的时候创建的
			Object bean = getBean(FACTORY_BEAN_PREFIX(&) + beanName);
		} else {
			//这里就会真的去创建Bean了
			getBean(beanName);
		}
	}
}

重新遍历 beanNames : beanName {
	根据beanName 获取对象
	if (instanceof SmartInitializingSingleton) 则执行 
		smartSingleton.afterSingletonsInstantiated(); 
		smartInitialize.end();
}
//这里算是生命周期扩展的一个补充 在所有非懒加载单例Bean初始化后执行的2个方法

getMergedLocalBeanDefinition(beanName)

根据beanName组装合并BenaDefinition(RootBeanDefinition)
最终执行逻辑的是:
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String, org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.config.BeanDefinition)

缓存池中获取mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd == null) {
	if (bd.getParentName() == null 且 不是 RootBeanDefinition) {
		mbd = 就根据现有的BeanDefinition创建RootBeanDefinition
	}
	if (bd.getParentName() != null) {
		mbd = 就把父子融合创建RootBeanDefinition
			1. 根据父BeanDefinition创建RootBeanDefinition
			2. 在根据子类BeanDefinition覆盖对应的信息
	}
}
//所以在Spring中常用的是融合后的RootBeanDefinition池子
//这里也起到归类的作用
//比如扫描出来的BeanDefinition是ScannedGenericBeanDefinition
//而根据配置类创建出来的是AnnotatedGenericBeanDefinition
//最终都在这里转化为RootBeanDefinition供创建Bean使用

创建Bean

doGetBean

分析的代码为:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

转化BeanNametransformedBeanName(name): beanName
//查看缓存中是否已经有beanName对应的实例了
Object sharedInstance = getSingleton(beanName);
//getObjectForBeanInstance 注释说了可以返回本身 或者 返回FactoryBean 时创建的对象
if (sharedInstance  != null ) {beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}
if (sharedInstance  == null ) {
	if (curVal = this.prototypesCurrentlyInCreation.get() 且 curVal等于或者包含 beanName) { 认为循环依赖,报错}
	获取父Bean工厂:parentBeanFactory
	if (parentBeanFactory != null 且 自身没有对应的BeanDefinition){
		//这里把beanName 恢复了一下 这个name是之前没有处理前的name,有可能带&
		//这里如果带了& 就用带& 处理
		String nameToLookup = originalBeanName(name);
		返回 parentBeanFactory.getBean(nameToLookup, args);
	}
	
	if (parentBeanFactory == nul){
		//获取RootBeanDefinition 因为在上一步已经有缓存了这里直接拿到了
		RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
		if (mbd.getDependsOn()有值) {创建DependsOn内的Bean}
		//不同scope调用不同的创建策略
		case 单例 :  
			sharedInstance = getSingleton(beanName, 创建Bean(createBean(beanName, mbd, args)));
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 
		break;
	}
	//这里其实就是看下Bean实例是不是你需要的类型
	//也就是 getBean(name, class)中的class类型
	//如果不是则调用Converter尝试进行转化
	return 适配BeanInstance
}

转化BeanName(transformedBeanName(name))

if (& 开头) {把所有的&去掉}
//这里就是查到别名映射的最终的BeanName
while(resolvedName = this.aliasMap.get(canonicalName) 且 resolvedName != null) {
	canonicalName = resolvedName;
}

createBean(beanName, mbd, args)

真实的创建Bean的逻辑
分析的代码是:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

获取 Class 的对象 : resolvedClass (resolveBeanClass(mbd, beanName))
//这里为啥要复制一个出来用 我的想法是避免别的地方修改原有的对象对我造成影响
重新赋值一个 RootBeanDefinition :mbdToUse
//这里之前setBeanClass是把类的name放进去了 在这里改为把Class对象放进去
mbdToUse.setBeanClass(resolvedClass);
//beforeInstantiationResolved 这个属性是RootBeanDefinition自己的 Spring默认是没有赋值的
if (mbd.beforeInstantiationResolved == null || == true) {
	//这里InstantiationAwareBeanPostProcessors是有值的
	if(hasInstantiationAwareBeanPostProcessors()) {
		获取目标Class类型 = mbd.getTargetType()this.resolvedTargetType
		//这里是给自定义Bean一个机会 如果这里有任何一个返回就直接作为最终Bean了
		//Spring 默认在这里没有返回的
		遍历处理器 InstantiationAwareBeanPostProcessor:bp{
			Object result =  bp.postProcessBeforeInstantiation(beanClass, beanName)
			if (result != null) {
				//这里赶紧再执行下初始化后置处理器方法
				//因为实例化已经被处理器拦截了 走不动后边了
				bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
				//这个对象就是放在容器里的Bean了
				return result ;
			}
		}
	}
	
	//没有被实例化前处理器拦截的化就进去正式的实例化
	BeanWrapper instanceWrapper = null;
	if(是单例) {instanceWrapper=从factoryBeanInstanceCache获取一下}
	if(instanceWrapper == null){
		instanceWrapper = 创建Bean实例(createBeanInstance(beanName, mbd, args)}
	//到这里 instanceWrapper 就默认不是null了 因为后续就直接用了
	mbd.resolvedTargetType = instanceWrapper.getWrappedClass();
	//这里的处理器是给修改 mbd 一个机会
	遍历处理器 MergedBeanDefinitionPostProcessor :processor {
		processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
	}
	
	//属性填充
	populateBean(beanName, mbd, instanceWrapper);
	//初始化
	exposedObject = initializeBean(beanName, exposedObject, mbd);
	//注册bean销毁相关
	registerDisposableBeanIfNecessary(beanName, bean, mbd);
	return exposedObject;
} 

resolveBeanClass(mbd, beanName)

根据className获取class

//这里其实beanClass还保存的ClassName 不是一个Class
if (mbd.beanClass instanceof Class) { return mbd.beanClass}
String className = mbd.beanClass;
evaluated = el表达式解析器解析处理下 className
if(className != null) {return beanClassLoader.loadClass(className);}
//到这里说明没有特别设置className那就正常解析
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
this.beanClass = resolvedClass;
return resolvedClass;

createBeanInstance(beanName, mbd, args)

//这一次经过上边的解析 mbd.beanClass 已经是个对象所以直接返回了
获取 Class 的对象 : resolvedClass (resolveBeanClass(mbd, beanName))
//这里行为参数化 你要自定义了实例方法就用你设置的实例创建方法创建
if (mbd.getInstanceSupplier() != null) {return Supplier.get()}
//默认没有值
if (mbd.getFactoryMethodName() != null) {return 指定工厂方法创建}
//这里大致意思就是 如果 args传值就用传入的值来推断下带参构造方法实例化
if (args == null && mbd.resolvedConstructorOrFactoryMethod != null) {return autowireConstructor(beanName, mbd, null, null);}
//如果构造器上有@Autowired
遍历处理器 SmartInstantiationAwareBeanPostProcessor bp {
	Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
	if (ctors != null) {
		return ctors;
	}
}
if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, args);}
//走到这里就是后置处理器没有设置 也没有带@Autowired 那就用配置的默认构造器
//这里Spring 还没想好咋实现 直接就是返回 null
if ((ctors = mbd.getPreferredConstructors()) != null) {return autowireConstructor(beanName, mbd, ctors, args);}
//到这里就是用 无参构造器创建了
return instantiateBean(beanName, mbd);

populateBean(beanName, mbd, instanceWrapper)

instanceWrapper 中包含了已经实例化的对象

遍历处理器 InstantiationAwareBeanPostProcessor bp {
	if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
		//如果到这里就阻断属性赋值流程了
		return;
	}
}
PropertyValues pvs = mbd.getPropertyValues();
//如果有后置处理器设置了PropertyValues 就把这个设置给pvs 默认是没有的
//但是这里有一个AutowiredAnnotationBeanPostProcessor会处理@Autowired、@Value
遍历处理器 InstantiationAwareBeanPostProcessor bp {
	PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
	pvs = pvsToUse;
}
if (pvs.isEmpty()) {return;}

initializeBean(beanName, exposedObject, mbd)

//这里提前处理了三个Aware
//BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
invokeAwareMethods(beanName, bean);

Object wrappedBean = bean;
遍历处理器 BeanPostProcessor processor {
	Object current = processor.postProcessBeforeInitialization(result, beanName);
	//current == null 直接 return
	wrappedBean = current;
}
if (bean instanceof InitializingBean) {bean.afterPropertiesSet();}
if ((String initMethodName = mbd.getInitMethodName()) != null) {调用InitMethodName}
//AOP就是在这里把对象替换成了代理对象
遍历处理器 BeanPostProcessor processor{
	Object current = processor.postProcessAfterInitialization(result, beanName);
	//current == null 直接 return
	wrappedBean = current;
}
return wrappedBean;

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

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

相关文章

HTML+CSS+JavaScript:轮播图自动播放

一、需求 轮播图如下图所示&#xff0c;需求是每隔一秒轮播图自动切换一次 二、代码素材 以下是缺失JS部分的代码&#xff0c;感兴趣的小伙伴可以先自己试着写一写 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /&…

【笔记】Java并发编程

Callable 和 Runable Java 8 并发教程&#xff1a;Threads和Executors 原文&#xff1a;Java 8 Concurrency Tutorial: Threads and Executors 译者&#xff1a;BlankKelly 来源&#xff1a;Java8并发教程&#xff1a;Threads和Executors 两种提交方式 submit和execute subm…

【数据结构】带头+双向+循环链表(DList)(增、删、查、改)详解

一、带头双向循环链表的定义和结构 1、定义 带头双向循环链表&#xff0c;有一个数据域和两个指针域。一个是前驱指针&#xff0c;指向其前一个节点&#xff1b;一个是后继指针&#xff0c;指向其后一个节点。 // 定义双向链表的节点 typedef struct ListNode {LTDataType dat…

开发一个RISC-V上的操作系统(五)—— 协作式多任务

目录 往期文章传送门 一、什么是多任务 二、代码实现 三、测试 往期文章传送门 开发一个RISC-V上的操作系统&#xff08;一&#xff09;—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统&#xff08;二&#xff09;—— 系统引导程序&a…

NO4 实验四:生成Web工程

1、说明 使用 mvn archetype&#xff1a;generate 命令生成 Web 工程时&#xff0c;需要使用一个专门的 archetype。这个专门生成 Web 工程骨架的 archetype 可以参照官网看到它的用法&#xff1a; 2、操作 注意&#xff1a;如果在上一个工程的目录下执行 mvn archetype&…

LeetCode208.Implement-Trie-Prefix-Tree<实现 Trie (前缀树)>

题目&#xff1a; 思路&#xff1a; tire树&#xff0c;学过&#xff0c;模板题。一种数据结构与算法的结合吧。 代码是&#xff1a; //codeclass Trie { private:bool isEnd;Trie* next[26]; public:Trie() {isEnd false;memset(next, 0, sizeof(next));}void insert(strin…

第72篇:近年HVV、红队攻防比赛中常见外围打点漏洞的分析与总结

前言 大家好&#xff0c;我是ABC_123。前一段时间我花时间花精力总结了最近两三年中&#xff0c;在攻防比赛中比较常见的Web打点漏洞类型&#xff0c;捎带着把钓鱼邮件的主题类型也总结了一下。今天分享出来&#xff0c;相信无论是对于攻击方还是防守方&#xff0c;都能从中获…

【雕爷学编程】MicroPython动手做(14)——掌控板之OLED屏幕2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

Spring学习笔记之spring概述

文章目录 Spring介绍Spring8大模块Spring特点 Spring介绍 Spring是一个轻量级的控制反转和面向切面的容器框架 Spring最初的出现是为了解决EJB臃肿的设计&#xff0c;以及难以测试等问题。 Spring为了简化开发而生&#xff0c;让程序员只需关注核心业务的实现&#xff0c;尽…

从源码角度去深入分析关于Spring的异常处理ExceptionHandler的实现原理

ExceptionHandler的作用 ExceptionHandler是Spring框架提供的一个注解&#xff0c;用于处理应用程序中的异常。当应用程序中发生异常时&#xff0c;ExceptionHandler将优先地拦截异常并处理它&#xff0c;然后将处理结果返回到前端。该注解可用于类级别和方法级别&#xff0c;…

【RabbitMQ】golang客户端教程1——HelloWorld

一、介绍 本教程假设RabbitMQ已安装并运行在本机上的标准端口&#xff08;5672&#xff09;。如果你使用不同的主机、端口或凭据&#xff0c;则需要调整连接设置。如果你未安装RabbitMQ&#xff0c;可以浏览我上一篇文章Linux系统服务器安装RabbitMQ RabbitMQ是一个消息代理&…

STL 关于vector的细节,vector模拟实现【C++】

文章目录 vector成员变量默认成员函数构造函数拷贝构造赋值运算符重载函数析构函数 迭代器beginend size和capacityresizereserve[ ]push_backpop_backinserteraseswap vector成员变量 _start指向容器的头&#xff0c;_finish指向容器当中有效数据的下一个位置&#xff0c;_end…

Python(五十一)获取列表中的多个元素——切片操作

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

【Rust教程 | 基础系列1 | Rust初相识】Rust简介与环境配置

教程目录 前言一&#xff0c;Rust简介1&#xff0c;Rust的历史2&#xff0c;Rust的特性3&#xff0c;为什么选择Rust4&#xff0c;Rust可以做什么 二&#xff0c; Rust环境配置1&#xff0c;windows11安装2&#xff0c;Linux安装 三&#xff0c;安装IDE 前言 Rust是一种系统编…

谈谈3D打印技术

目录 1.什么是3D打印 2.3D打印与传统打印技术的不同之处 3. 3D打印带来的技术变革 1.什么是3D打印 3D打印技术&#xff0c;也称为增材制造&#xff08;Additive Manufacturing&#xff09;&#xff0c;是一种将数字模型转化为实体物体的制造方法。它通过逐层添加材料的方式&a…

一文了解MySQL中的多版本并发控制

在开始之前&#xff0c;先抛出一个问题&#xff1a;我们都知道&#xff0c;目前&#xff08;MySQL 5.6以上&#xff09;数据库已普遍使用InnoDB存储引擎&#xff0c;InnoDB相对于MyISAM存储引擎其中一个好处就是在数据库级别锁和表级别锁的基础上支持了行锁&#xff0c;还有就是…

windows环境安装elasticsearch+kibana并完成JAVA客户端查询

下载elasticsearch和kibana安装包 原文连接&#xff1a;https://juejin.cn/post/7261262567304298554 elasticsearch官网下载比较慢&#xff0c;有时还打不开&#xff0c;可以通过https://elasticsearch.cn/download/下载&#xff0c;先找到对应的版本&#xff0c;最好使用迅…

24考研数据结构-第二章:线性表

目录 第二章&#xff1a;线性表2.1线性表的定义&#xff08;逻辑结构&#xff09;2.2 线性表的基本操作&#xff08;运算&#xff09;2.3 线性表的物理/存储结构&#xff08;确定了才确定数据结构&#xff09;2.3.1 顺序表的定义2.3.1.1 静态分配2.3.1.2 动态分配2.3.1.3 mallo…

MacOS Monterey VM Install ESXi to 7 U2

一、MacOS Monterey ISO 准备 1.1 下载macOS Monterey 下载&#x1f517;链接 一定是 ISO 格式的&#xff0c;其他格式不适用&#xff1a; https://www.mediafire.com/file/4fcx0aeoehmbnmp/macOSMontereybyTechrechard.com.iso/file 1.2 将 Monterey ISO 文件上传到数据…

更简单的读取和存储对象 (Bean)

怎样才能比之前更简单的 读取和存储对象 (Bean) 呢? 答: 就两个字"使用注解", 接下来就说说如何利用注解来更简单的操作 Bean 目录 一. 前置工作 (配置扫描路径) 二. 使用注解存储 Bean 2.1 注解介绍 2.1.1 类注解存储 Bean 的默认命名规则 2.2 Controller (控…