Spring之生成Bean

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) {// 获取合并后的BeanDefinitionRootBeanDefinition 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找 是否有该beanNameif (!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为valueregisterDependentBean(dep, beanName);// 创建所依赖的beantry {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()*/@Overridepublic 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.rhkb.cn/news/436452.html

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

相关文章

Azure Data Box 80 TB 现已在中国区正式发布

我们非常高兴地宣布&#xff0c;Azure Data Box 80 TB SKU现已在 Azure 中国区正式发布。Azure Data Box 是 Azure 的离线数据传输解决方案&#xff0c;允许您以快速、经济且可靠的方式将 PB 级数据从 Azure 存储中导入或导出。通过硬件传输设备可加速数据的安全传输&#xff0…

NVIDIA G-Assist 项目:您的游戏和应用程序AI助手

NVIDIA G-Assist 是一个革命性的人工智能助手项目&#xff0c;旨在通过先进的AI技术提升玩家的游戏体验和系统性能。这个项目在2024年Computex上首次亮相&#xff0c;展示了其在游戏和应用程序中的潜在应用。 喜好儿网 G-Assist 的核心功能是提供上下文感知的帮助。它能够接收…

用示波器测动态滞回线

大学物理&#xff08;下&#xff09;实验-中南民族大学通信工程2022级 手动逐个处理数据较为麻烦且还要绘图&#xff0c;故想到用pythonmatplotlib来计算结果并数据可视化。 代码实现 import matplotlib.pyplot as plt# 样品一磁化曲线 X [0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2.…

云计算:MySQL

第一周第一天-MySQL的SQL语句解析 数据库的介绍 什么是数据库 数据库是存储和管理数据的系统或集合&#xff0c;通常用于支持软件系统的高效数据处理和查询。它能够以结构化的方式组织数据&#xff0c;使用户可以快速存储、更新、查询和删除数据。数据库不仅保存数据&#xff0…

【数学分析笔记】第4章第2节 导数的意义和性质(1)

4. 微分 4.2 导数的意义与性质 4.2.1 导数在物理中的背景 物体在OS方向上运动&#xff0c;位移函数为 s s ( t ) ss(t) ss(t)&#xff0c;求时刻 t t t的瞬时速度&#xff0c;找一个区间 [ t , t △ t ] [t,t\bigtriangleup t] [t,t△t]&#xff0c;从时刻 t t t变到时刻 t…

2024年9月26日--- Spring-AOP

SpringAOP 在学习编程过程中&#xff0c;我们对于公共方法的处理应该是这样的一个过程&#xff0c;初期阶段如下 f1(){Date now new Date();System.out.println("功能执行之前的各种前置工作"now)//...功能代码//...功能代码System.out.println("功能执行之前…

vue3使用Teleport 控制台报警告:Invalid Teleport target on mount: null (object)

Failed to locate Teleport target with selector “.demon”. Note the target element must exist before the component is mounted - i.e. the target cannot be rendered by the component itself, and ideally should be outside of the entire Vue component tree main.…

OpenStack Yoga版安装笔记(十五)Horizon安装

1、官方文档 OpenStack Installation Guidehttps://docs.openstack.org/install-guide/ 本次安装是在Ubuntu 22.04上进行&#xff0c;基本按照OpenStack Installation Guide顺序执行&#xff0c;主要内容包括&#xff1a; 环境安装 &#xff08;已完成&#xff09;OpenStack…

ndb9300public-ndb2excel简介

1 引言 ndb9300是一个自己定义的机载导航数据库劳作&#xff08;不敢称为项目&#xff09;代号&#xff0c;其中3表示是第3种数据库。 多年前&#xff0c;对在役民航客机中的某型机载导航数据库的二进制文件进行分析&#xff0c;弄明白它的数据结构后做了几个工具&#xff0c…

仿真设计|基于51单片机的土壤温湿度监测及自动浇花系统仿真

目录 具体实现功能 设计介绍 51单片机简介 资料内容 仿真实现&#xff08;protues8.7&#xff09; 程序&#xff08;Keil5&#xff09; 全部内容 资料获取 具体实现功能 &#xff08;1&#xff09;DS18B20实时检测环境温度&#xff0c;LCD1602实时显示土壤温湿度&…

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理> 文章目录 <使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>1.冒泡排序实现形式总结1.1关于冒泡排序实现形式1的来源&#xff1a;1.2对四种排序实现形式使用AI进行无引导分析&#xff1a;1.3AI&…

正交阵的概念、性质与应用

正交阵是线性代数中一种重要的特殊矩阵&#xff0c;它在很多领域都有广泛的应用。 1. 概念 一个实数方阵 Q 被称为正交阵&#xff0c;如果它的转置等于它的逆矩阵&#xff1a; 这意味着&#xff1a; 其中&#xff0c;Q T 表示矩阵 Q 的转置&#xff0c;I 表示单位矩阵。 2…

Linux:磁盘管理

一、静态分区管理 静态的分区方法不可以动态的增加或减少分区的容量。 1、磁盘分区-fdisk 该命令是用于查看磁盘分区情况&#xff0c;和分区管理的命令 命令格式&#xff1a;fdisk [选项] 设备文件名常用命令&#xff1a; -h&#xff1a;查看分区信息 fdisk系统常用命令&…

GIT安装及集成到IDEA中操作步骤

最近深感GIT使用技能太差&#xff0c;我只会些皮毛&#xff0c;还是得看官网&#xff0c;总结一下常用的操作方法吧。 GIT环境配置到IDEA中安装 一、GIt的基本的安装 这个不在这里赘述了&#xff0c;自己装一个git吧 二、给IDEA指定本地GIT的安装路径 1、下图这个是我本地的…

05-函数传值VS传引用

函数传值 一、没法改变值的方式&#xff1a; 一个变量拷贝到另一个变量, 这种形式的函数调用被称为: 传值调用 局部变量的生命周期在函数的运行期间会一直存在. void Increment(int a)//假设一个 x(只是为了验证实参会被映射到形参这件事情),a的值会被拷贝到x {a a 1; //1…

vscode开发uniapp安装插件指南

安装vuets的相关插件 首先是vue的相关插件&#xff0c;目前2024年9月应该是vue-offical 安装uniapp开发插件 uni-create-view &#xff1a;快速创建 uni-app 页面 安装uni-create-view之后修改插件拓展设置 勾选第一个选择创建视图时创建同名文件夹 选择第二个创建文件夹中生…

【RockyLinux 9.4】安装新版 QQ for Linux(不再是 QQ2008 那种老款了!)

总览 还记得两年之前的时候&#xff0c;当初用的还是那种 QQ2008 一样的 LinuxQQ 啥也干不了&#xff0c;还不如 QQ2008 最近寻思自己装个服务器玩&#xff0c;想下载一个 QQ 用来文件传输&#xff0c;没想到现在的 QQ Linux 这么棒&#xff01; 一、下载 1.下载网址 https…

神经网络激活函数

神经网络的激活函数&#xff08;Activation Function&#xff09; 神经网络可以用在分类问题和回归问题上&#xff0c;不过需要根据情况改变输出层的激活函数。一般而言&#xff0c;回归问题用恒等函数&#xff0c;分类问题用softmax函数。 神经网络的激活函数必须使用非线性函…

Trilium Notes笔记本地化部署与简单使用指南打造个人知识库

文章目录 前言1. 安装docker与docker-compose2. 启动容器运行镜像3. 本地访问测试4.安装内网穿透5. 创建公网地址6. 创建固定公网地址 前言 今天和大家分享一款在G站获得了26K的强大的开源在线协作笔记软件&#xff0c;Trilium Notes的中文版如何在Linux环境使用docker本地部署…

828华为云征文 | 利用FIO工具测试Flexus云服务器X实例存储性能

目录 一、Flexus云服务器X实例概要 1.1 Flexus云服务器X实例摘要 1.2 产品特点 1.3 存储方面性能 1.4 测评服务器规格 二、FIO工具 2.1 安装部署FIO 2.2 主要性能指标概要 三、进行压测 3.1 测试全盘随机读IO延迟 3.2 测试全盘随机写IO延迟 3.3 测试随机读IOPS 3.4…