Spring之生成Bean

news2024/11/20 20:17:58

Bean的生命周期:实例化->属性填充->初始化->销毁

核心入口方法:finishBeanFactoryInitialization-->preInstantiateSingletons

DefaultListableBeanFactory#preInstantiateSingletons用于实例化非懒加载的bean。

1.preInstantiateSingletons方法分析

作用:实例化非懒加载的bean

1.拿到所有BeanDefinitionName,遍历所有beanName

2.getMergedLocalBeanDefinition,将属性合并生成MergeBeanDefinition,如下图的属性

最终结果是:Map<beanName,RootBeanDefinition>,beanName对应合并后的BeanDefintion

如果存在父子BeanDefinition,则要合并,二合一会生成第三个BeanDefinition(新的);否则,就直接返回自己的BeanDefinition。

如果该BeanDefinition是RootBeanDefinition,就直接cloneBeanDefinition,并返回。

同时,涉及到递归合并,合并的父亲又有父亲beanDefinition,就需要多次合并。(这段源码的含义)

// 子BeanDefinition的属性覆盖父BeanDefinition的属性,这就是合并
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);

3.会判断是不是懒加载、是不是单例Bean、是不是抽象BeanDefinition,如果都不是,则进入步骤4

4.如果是FactoryBean,则开始创建FactoryBean。FactoryBean对应两个bean对象,但是只有一个beanDefinition。核心代码如下:

for (String beanName : beanNames) {
    // 获取合并后的BeanDefinition
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    // isAbstract 抽象的BeanDefinition和抽象类无关
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        if (isFactoryBean(beanName)) {
            // 获取FactoryBean对象
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
                FactoryBean<?> factory = (FactoryBean<?>) bean;
                boolean isEagerInit;
                // 判断是否打开了安全管理器
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
                }
                else {
                    // 实现了接口SmartFactoryBean,并且isEagerInit=true
                    // 实现了接口FactoryBean,重写的getObject()方法 是在用户使用getBean的时候调用的
                    // SmartFactoryBean可以指定在 bean创建的时候 调用getObject()方法
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                   ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    // 创建真正的Bean对象(getObject()返回的对象)
                    getBean(beanName);
                }
            }
        }
        else {
            // 创建Bean对象
            getBean(beanName);
        }
    }
}

如果我们从单例池拿到了bean,那么就要判断是否和我们想要的bean类型一样(FactoryBean还是普通bean)。从Cache拿bean,getCachedObjectForFactoryBean(存放getObject生成的bean)

下面的代码:判断该bean是不是FactoryBean。

@Override
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null) {
        return (beanInstance instanceof FactoryBean);
    }
    // No singleton instance found -> check bean definition.
    // 去父beanFactory找 是否有该beanName
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
        // No bean definition found in this factory -> delegate to parent.
        return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
    }
    return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}

注意:如果FactoryBean类实现了SmartFactoryBean,并且重写了isEagerInit()方法,则在bean生命周期中就会创建FactoryBean,否则在getBean的时候才会创建FactoryBean。

5.不是FactoryBean且是非懒加载的bean

调用getBean,创建Bean

6.所有的非懒加载单例Bean都创建完了后,getSingleton(beanName);拿出所有单例Bean,看其是否实现了SmartInitializingSingleton接口,如果实现了这个接口,就执行重写后的方法:afterSingletonsInstantiated。

afterSingletonsInstantiated的调用时机:所有非懒加载的bean都创建完了之后,调用它

2.getBean

首先,getBean有三种调用方式:

name, name+type(验证类型,可类型转换),name+args[](args[]用于推断构造方法)

getBean的核心方法是doGetBean

1.transformedBeanName

拿到BeanName,transformedBeanName就是去掉&符号,以及将别名转换为主名

示例1:@Bean({{"userService","userService1", "userService2"}):第一个名字是名字,后面的都是别名

示例2:&factoryBeanName是拿factoryBeanName;而factoryBeanName是拿getObject的bean。(比如UserBean)

2.getSingleton

先去单例池拿bean,拿到了就检查符不符合我们的需求(原型也走这一步)

3.拿不到,则进入else分支,创建bean

4.进入这行代码,parentBeanFactory != null && !containsBeanDefinition(beanName)

检查当前是否存在这个beanName的BeanDefinition,也要检查父BeanFactory有没有BeanDefinition,有就返回bean

5.核心开始,getMergedLocalBeanDefinition,拿到合并的BeanDefinition

6.checkMergedBeanDefinition

检查BeanDefinition是不是Abstract的,如果是,则不能创建bean(抽象的beanDefinition不能创建bean)

7.getDependsOn

处理@DepondsOn注解。检查依赖,将其挂载到该bean的依赖属性上,然后创建依赖的bean,如果创建失败,则会抛出异常。

存在一些依赖,就将依赖关系存入map(使用registerDependentBean方法),然后getBean(创建所依赖的bean,此时也会判断是否存在循环依赖的关系)

[比如类A上加了@DepondsOn("B"),则在创建A之前必须把B创建出来。如果类B也通过这种方式依赖了A,那么就产生了循环依赖,这种情况的循环依赖会直接抛出异常,无法解决!]

源码如下:

            // Guarantee initialization of beans that the current bean depends on.
		        String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					// dependsOn表示当前beanName所依赖的,当前Bean创建之前dependsOn所依赖的Bean必须已经创建好了
					for (String dep : dependsOn) {
						// beanName是不是被dep依赖了,如果是则出现了循环依赖
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// dep被beanName依赖了,存入dependentBeanMap中,dep为key,beanName为value
						registerDependentBean(dep, beanName);

						// 创建所依赖的bean
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

8.判断bean是单例、原型、还是其他,然后进入不同if分支

创建单例Bean,执行Lambda表达式,创建Bean后需要放到单例池;之后还会创建factoryBean

创建原型Bean,不会将其放到单例池,仅仅是简单的创建;之后还会创建factoryBean

对于request、session、application,例如,在同一个request中,同一个BeanName拿到的是用一个Bean。主要通过这两个方法,request.getAttribute,获取bean;request.setAttribute,设置bean。SpringMVC在启动的时候,会将Scope注册到容器中,之后就可以直接拿到scope的值。

9.核心是创建单例Bean

sharedInstance = getSingleton(beanName, () -> {...})

从单例池找Bean,如果没有找到Bean,就使用Lambda表达式创建Bean。addSingleton(并添加到单例池)

核心方法:createBean

执行Lambda的核心代码:getSingleton

try {
	singletonObject = singletonFactory.getObject(); // 执行Lambda表达式
	newSingleton = true;
}

3.createBean

1.resolveBeanClass

马上就要实例化Bean了,确保beanClass被加载了

// 如果beanClass被加载了
if (mbd.hasBeanClass()) {
   return mbd.getBeanClass();
}

// 如果beanClass没有被加载
if (System.getSecurityManager() != null) {
   return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)
         () -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
else {
   return doResolveBeanClass(mbd, typesToMatch);// 加载类
}

2.resolveBeforeInstantiation

实例化前,执行实例化前的PostProcessor。

如果bean实现了InstantiationAwareBeanPostProcessor接口,并重写了invokeAwareMethods,可以在这里直接返回Bean,终止Spring创建该Bean(可以进行人工干预来创建bean)。如果在实例化前就返回了Bean,那么他需要做初始化后的操作。(和AOP有关)

3.doCreateBean

实例化,主要流程是下面的步骤

4.createBeanInstance

实例化Bean

5.applyMergedBeanDefinitionPostProcessors

该后置处理器的执行时机:实例化后,初始化前;作用:后置处理合并后的BeanDefinition

6.解决循环依赖

7.populateBean

作用:属性填充

在里面执行实例化后的方法(postProcessAfterInstantiation),

然后执行Spring自带的属性注入(ByType和ByName),

然后执行(InstantiationAwareBeanPostProcessor中的) AutowiredAnnotationBeanPostProcessor.postProcessProperties(),处理@Autowired注解。(处理属性注入的,@Autowired注解就是通过实现这个接口实现的依赖注入)

注意:在属性赋值之前,如果程序员已经为一些属性赋值了,那么就不会再重复赋值

byName 还是 byType导致注入太灵活,只要是个set方法就会被注入,容易出问题吧。

8.initializeBean

作用:初始化Bean

核心流程:

8.1invokeAwareMethods(包含设置beanName,bean类加载器、bean工厂:setBeanName、setBeanClassLoader、setBeanFactory)

8.2执行applyBeanPostProcessorsBeforeInitialization:初始化前的PostProcessor,包含使用@PostContruct注解和实现回调接口两种方法

初始化前有很多PostProcessor,比如下面这些

8.3invokeInitMethods:判断有无实现对应接口,执行初始化操作(是否实现InitializingBean)

8.4applyBeanPostProcessorsAfterInitialization:执行初始化后PostProcessor

9.判断初始化后的bean的依赖关系有没有改变,如果有改变,则可能会报错

和AOP有关,如果AOP之后产生新的bean,但是注入给其他bean的是原来的bean,并非最终生成的bean,那么在这里就会抛出异常

10.registerDisposableBeanIfNecessary

销毁Bean,判断bean是否有bean销毁的逻辑

4.bean销毁

销毁Bean的两个核心方法:

registerDisposableBeanIfNecessary:向Adapter中设置销毁逻辑。(适配器模式)

context.close():调用Adpter的销毁逻辑。
实现Bean销毁的方法

1.实现DisposableBean接口,重写destroy方法;

2.使用PreDestroy注解

4.1.registerDisposableBeanIfNecessary

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {// 判断是否实现了销毁的接口
			if (mbd.isSingleton()) {
				// Register a DisposableBean implementation that performs all destruction
				// work for the given bean: DestructionAwareBeanPostProcessors,
				// DisposableBean interface, custom destroy method.
				registerDisposableBean(beanName, new DisposableBeanAdapter(
						bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
			}
			else {
				// A bean with a custom scope...
				Scope scope = this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
						bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
			}
		}
	}

1.判断有没有指定销毁的方法

2.hasDestroyMethod

有无实现接口DisposableBean、AutoCloseable

3.hasDestructionAwareBeanPostProcessors

有没有实现DestructionAwareBeanPostProcessor,有没有重写requiresDestruction,重写了这个接口的方法,返回true,就需要销毁。

4.registerDisposableBean

把销毁逻辑存到disposableBeans,他是一个Map,Map<beanName,Adapter>。

这里用到适配器模式:new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)

DisposableBeanAdapter中的核心方法:destroy

4.2.context.close()

/**
	 * Close this application context, destroying all beans in its bean factory.
	 * <p>Delegates to {@code doClose()} for the actual closing procedure.
	 * Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
	 * @see #doClose()
	 * @see #registerShutdownHook()
	 */
	@Override
	public void close() {
		synchronized (this.startupShutdownMonitor) {
			doClose();
			// If we registered a JVM shutdown hook, we don't need it anymore now:
			// We've already explicitly closed the context.
			if (this.shutdownHook != null) {
				try {
					Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
				}
				catch (IllegalStateException ex) {
					// ignore - VM is already shutting down
				}
			}
		}
	}

核心方法是:doClose,调用Adpter的销毁逻辑

1.publishEvent

发布关闭事件

2.this.lifecycleProcessor.onClose();

关闭Spring容器的生命周期

3.destroyBeans

销毁Bean,以下步骤都是这个方法的操作。

4.destroySingletons-->destroySingleton

从单例池中移除掉Bean,如果存在依赖关系,则也要进行相应销毁

比如,A依赖B,那么B如果需要销毁,那么先销毁A,再销毁B

在这个方法中,会调用bean.destroy(),这里就进到了Adpter的销毁逻辑

5.遍历有销毁逻辑的Bean

this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();

执行clearSingletonCache,包含以下内容:

this.singletonObjects.clear(); 单例池
this.singletonFactories.clear();
this.earlySingletonObjects.clear();
this.registeredSingletons.clear();
this.singletonsCurrentlyInDestruction = false;

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

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

相关文章

【RADARSAT Constellation Mission(RCM)卫星星座简介】

RADARSAT Constellation Mission&#xff08;RCM&#xff09;卫星星座是加拿大太空局&#xff08;CSA&#xff09;的下一代C波段合成孔径雷达&#xff08;SAR&#xff09;卫星星座&#xff0c;以下是对其的详细介绍&#xff1a; 一、基本信息 发射时间&#xff1a;2019年6月…

Golang | Leetcode Golang题解之第452题用最少数量的箭引爆气球

题目&#xff1a; 题解&#xff1a; func findMinArrowShots(points [][]int) int {if len(points) 0 {return 0}sort.Slice(points, func(i, j int) bool { return points[i][1] < points[j][1] })maxRight : points[0][1]ans : 1for _, p : range points {if p[0] > …

秒懂Linux之线程

目录 线程概念 线程理解 地址空间&#xff08;页表&#xff0c;内存&#xff0c;虚拟地址&#xff09; 线程的控制 铺垫 线程创建 ​编辑 线程等待 线程异常 线程终止 代码 线程优点 线程缺点 线程特点 线程概念 线程是进程内部的一个执行分支&#xff0c;线程是C…

小程序-全局数据共享

目录 1.什么是全局数据共享 2. 小程序中的全局数据共享方案 MboX 1. 安装 MobX 相关的包 2. 创建 MobX 的 Store 实例 3. 将 Store 中的成员绑定到页面中 4. 在页面上使用 Store 中的成员 5. 将 Store 中的成员绑定到组件中 6. 在组件中使用 Store 中的成员 1.什么是全…

【LeetCode】每日一题 2024_10_2 准时到达的列车最小时速(二分答案)

前言 每天和你一起刷 LeetCode 每日一题~ 大家国庆节快乐呀~ LeetCode 启动&#xff01; 题目&#xff1a;准时到达的列车最小时速 代码与解题思路 今天这道题是经典的二分答案&#xff0c;结合这道题来讲就是&#xff0c;二分列车的速度 我最擅长的两个算法&#xff1a;一…

cGANs with Projection Discriminator

基于映射鉴别器的CGAN 模型中&#xff0c;判别器&#xff08;Discriminator&#xff09;不是通过将条件信息简单地与特征向量拼接&#xff08;concatenate&#xff09;来使用条件信息&#xff0c;而是采用一种基于投影的方式&#xff0c;这种方式更加尊重条件信息在底层概率模…

进程通信——内存映射

进程通信——内存映射 什么是内存映射 内存映射是一种将文件内容映射到进程地址空间的技术&#xff0c;使得进程可以直接访问文件内容&#xff0c;而不需要通过系统调用进行读写操作。内存映射可以提高文件访问的效率&#xff0c;并且可以实现进程间的通信。 内存映射的原理…

【HarmonyOS】时间处理Dayjs

背景 在项目中经常会使用要时间的格式转换&#xff0c;比如数据库返回一个Date数据&#xff0c;你需要转成2024-10-2的格式&#xff0c;鸿蒙的原生SDK中是没有办法实现的&#xff0c;因此&#xff0c;在这里介绍第三方封装好并且成熟使用的库Dayjs。 安装 切换到Entry文件夹下…

C++初学者指南-5.标准库(第二部分)–特殊迭代器

C初学者指南-5.标准库(第二部分)–特殊迭代器 文章目录 C初学者指南-5.标准库(第二部分)–特殊迭代器容器操纵器std::insert_iterator\<Container>std::back_insert_iterator\<Container>std::front_insert_iterator\<Container> I/O 流迭代器std::istream_i…

2024大二上js高级+ES6学习9.29(深/浅拷贝,正则表达式,let/const,解构赋值,箭头函数,剩余参数)

9.29.2024 1.浅拷贝和深拷贝 Es6的语法糖&#xff1a;用assign将obj对象浅拷贝给o对象。 把数组写在前面是因为数组也是对象 2.正则表达式 创建和检测正则表达式 正则表达式的使用直接跳过&#xff0c;等要用时现查现用 3.ES6 4.let关键字 块级作用域是指在一个{}l里 变量提…

Python | Leetcode Python题解之第441题排列硬币

题目&#xff1a; 题解&#xff1a; class Solution:def arrangeCoins(self, n: int) -> int:left, right 1, nwhile left < right:mid (left right 1) // 2if mid * (mid 1) < 2 * n:left midelse:right mid - 1return left

四、Java 基础语法

一、Java 的类、对象、方法和实例变量 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。对象&#xff1a;对象是类的一个实例&#xff0c;有状态&#xff08;实例变量&#xff…

MySQL基础练习题49-低质量的问题

目录 题目 准备数据 分析数据 总结 题目 找出 低质量 问题的 ID 集合。如果一个力扣问题的喜欢率&#xff08;喜欢数除以总投票数&#xff09;严格低于 60% &#xff0c;则该问题为低质量问题。 按 problem_id 升序排列返回结果表。 准备数据 Create table If Not Exis…

深度学习基础—卷积神经网络示例

1.卷积神经网络的结构 在之前的博客《深度学习—简单的卷积神经网络》&#xff0c;仅由卷积层构成网络的全部&#xff0c;这还不是标准的网络结构&#xff0c;本文将继续介绍标准的卷积神经网络结构有哪些&#xff1f; 深度学习基础—简单的卷积神经网络https://blog.csdn.net…

STM32-按键控制LED 光敏传感器控制蜂鸣器(江协笔记)

1、按键 2、常见传感器模块 R1 定值电阻 N1 传感器电阻。对于光敏传感器来说&#xff0c;相当于光敏电阻&#xff1b;...... C(滤波电容) 给中间的电压输出进行滤波&#xff0c;用于滤除一些干扰&#xff0c;保证输出电压波形的平滑&#xff08;保持电路稳定&#xff09; …

Pikachu-暴力破解-验证码绕过(on client)

访问页面&#xff0c; 从burpsuite 上看到返回的源代码&#xff1b; 验证码生成时通过 createCode 方法生成&#xff0c;在前端页面生成&#xff1b; 同时也是在前端做的校验&#xff1b; 直接验证&#xff1b;F12 -- 网络&#xff0c;随便输入个账号、密码、验证码&#xff0…

多维度柱状图绘制

图形结果 绘制过程 数据如下 调整柱子宽度 Z轴设置 、 配色表

开源链动2+1模式AI智能名片S2B2C商城小程序源码:流量运营中的价值创造与用户影响

摘要&#xff1a;本文深入探讨在开源链动21模式AI智能名片S2B2C商城小程序源码的背景下&#xff0c;流量的激活、信任建立、圈层沉淀以及裂变等流量运营现象。分析流量运营成本与用户消费意识的关系&#xff0c;强调内容在赋予流量价值以影响用户感知和消费判断方面的重要性。 …

基于yolov8深度学习的120种犬类检测与识别系统python源码+onnx模型+评估指标曲线+精美GUI界面目标检测狗类检测犬类识别系统

【算法介绍】 基于YOLOv8深度学习的120种犬类检测与识别系统是一款功能强大的工具&#xff0c;该系统利用YOLOv8深度学习框架&#xff0c;通过21583张图片的训练&#xff0c;实现了对120种犬类的精准检测与识别。 该系统基于Python与PyQt5开发&#xff0c;具有简洁的UI界面&a…

当AI成为作家,人工智能在写作领域的崛起

AI写作技术的应用正在多个领域展现出其强大的潜力和价值&#xff0c;它不仅极大地提升了内容创作的效率&#xff0c;还为创作者提供了一个全新的创作伙伴。 随着技术的进步&#xff0c;AI写作工具越来越能够理解复杂的语境和用户需求&#xff0c;帮助创作者生成高质量的内容。…