Spring Bean 的生命周期和获取方式

优质博文:IT-BLOG-CN

一、Spring Bean 的生命周期,如何被管理的

对于普通的 Java对象,当 new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而由 Spring IoC容器托管的对象,它们的生命周期完全由容器控制。Spring 中每个 Bean的生命周期如下:

主要对几个重要的步骤进行说明:
【1】实例化 Bean: 对于 BeanFactory容器,当客户向容器请求一个尚未初始化的 bean时,或初始化 bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean进行实例化。对于 ApplicationContext容器,当容器启动结束后,便实例化所有的单实例 bean。容器通过获取 BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。实例化对象被包装在 BeanWrapper 对象中,BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制设置属性。通过工厂方法或者执行构造器解析执行即可:创建的对象是个空对象。
【2】设置对象属性(依赖注入): 实例化后的对象被封装在 BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。紧接着获取所有的属性信息通过 populateBean(beanName,mbd,bw,pvs),Spring 根据 BeanDefinition 中的信息进行依赖注入。并且通过 BeanWrapper提供的设置属性的接口完成依赖注入。赋值之前获取所有的 InstantiationAwareBeanPostProcessor 后置处理器的 postProcessAfterInstantiation() 第二次获取InstantiationAwareBeanPostProcessor 后置处理器;执行 postProcessPropertyValues()最后为应用 Bean属性赋值:为属性利用 setter 方法进行赋值 applyPropertyValues(beanName,mbd,bw,pvs)。
【3】bean 初始化: initializeBean(beanName,bean,mbd)。
 1)执行xxxAware 接口的方法,调用实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口的方法。
 2)执行后置处理器之前的方法:applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)所有后置处理器的 BeanPostProcessor.postProcessBeforeInitialization()。
 3)执行初始化方法: InitializingBean 与 init-methodinvoke 当 BeanPostProcessor的前置处理完成后就会进入本阶段。先判断是否实现了 InitializingBean接口的实现;执行接口规定的初始化。其次自定义初始化方法。

InitializingBean 接口只有一个函数:afterPropertiesSet()这一阶段也可以在 bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前 bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。若要使用它,我们需要让 bean实现该接口,并把要增加的逻辑写在该函数中。然后 Spring会在前置处理完成后检测当前 bean是否实现了该接口,并执行 afterPropertiesSet函数。当然,Spring 为了降低对客户代码的侵入性,给 bean的配置提供了 init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring 便会在初始化阶段执行我们设置的函数。init-method 本质上仍然使用了InitializingBean接口。
 4)applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);执行初始化之后的后置处理器的方法。BeanPostProcessor.postProcessAfterInitialization(result, beanName);
【4】Bean的销毁: DisposableBean 和 destroy-method:和 init-method 一样,通过给 destroy-method 指定函数,就可以在bean 销毁前执行指定的逻辑。

Bean 的管理就是通过 IOC容器中的 BeanDefinition信息进行管理的。

二、Spring Bean 的加载和获取过程

Bean 的加载过程,主要是对配置文件的解析,并注册 bean 的过程 。

【1】根据注解或者 XML中 定义 Bean 的基本信息。例如:spring-core.xml

<bean id="myBean" class="com.taobao.pojo"></bean>

【2】获取配置文件:这里使用最原始的方式获取。

Resource resource = new ClassPathResource("spring-core.xml")

【3】 利用 XmlBeanFactory 解析并注册 bean 定义:已经完成将配置文件包装成了 Spring 定义的资源,并触发解析和注册。XmlBeanFactory 实际上是对 DefaultListableBeanFactory(非常核心的类,它包含了基本 IOC 容器所具有的重要功能,是一个 IOC 容器的基本实现。然后是调用了this.reader.loadBeanDefinitions(resource),从这里开始加载配置文件) 和 XmlBeanDefinitionReader 组合使用方式的封装,所以这里我们仍然将继续分析基于 XmlBeanFactory 加载 bean 的过程。

XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

Spring 使用了专门的资源加载器对资源进行加载,这里的 reader 就是 XmlBeanDefinitionReader 对象,专门用来加载基于 XML 文件配置的 bean。这里的加载过程为:

①、利用 EncodedResource 二次包装资源文件;
②、获取资源输入流,并构造 InputSource 对象:

// 获取资源的输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
// 构造InputSource对象
InputSource inputSource = new InputSource(inputStream);
// 真正开始从 XML文件中加载 Bean定义
return this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

这里的 this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 就是真正开始加载 XMl 的入口,该方法源码如下:第一步获取 org.w3c.dom.Document 对象,第二步由该对象解析得到 BeanDefinition 对象,并注册到 IOC 容器中。

protected intdoLoadBeanDefinitions(InputSource inputSource, Resource resource){try {// 1. 加载xml文件,获取到对应的Document(包含获取xml文件的实体解析器和验证模式)Document doc = this.doLoadDocument(inputSource, resource);// 2. 解析Document对象,并注册beanreturn this.registerBeanDefinitions(doc, resource);} 
}

③、获取 XML 文件的实体解析器和验证模式:this.doLoadDocument(inputSource, resource) 包含了获取实体解析器、验证模式,以及 Document 对象的逻辑,XML 是半结构化数据,XML 的验证模式用于保证结构的正确性,常见的验证模式有 DTD 和 XSD 两种。
④、加载 XML 文件,获取对应的 Document 对象和验证模式与解析器,解析器就可以加载 Document 对象了,这里本质上调用的是 DefaultDocumentLoader 的 loadDocument() 方法,源码如下:整个过程类似于我们平常解析 XML 文件的流程。

public DocumentloadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);
}

⑤、由 Document 对象解析并注册 bean:完成了对 XML 文件的到 Document 对象的解析,我们终于可以解析 Document 对象,并注册 bean 了,这一过程发生在 this.registerBeanDefinitions(doc, resource) 中,源码如下:

public intregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{// 使用DefaultBeanDefinitionDocumentReader构造BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();// 记录之前已经注册的BeanDefinition个数int countBefore = this.getRegistry().getBeanDefinitionCount();// 加载并注册beandocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 返回本次加载的bean的数量return getRegistry().getBeanDefinitionCount() - countBefore;
}

这里方法的作用是创建对应的 BeanDefinitionDocumentReader,并计算返回了过程中新注册的 bean 的数量,而具体的注册过程,则是由 BeanDefinitionDocumentReader 来完成的,具体的实现位于子类 DefaultBeanDefinitionDocumentReader 中:

publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){this.readerContext = readerContext;// 获取文档的root结点Element root = doc.getDocumentElement();this.doRegisterBeanDefinitions(root);
}

还是按照 Spring 命名习惯,doRegisterBeanDefinitions 才是真正干活的地方,这也是真正开始解析配置的核心所在:

protectedvoiddoRegisterBeanDefinitions(Element root){BeanDefinitionParserDelegate parent = this.delegate;this.delegate = this.createDelegate(getReaderContext(), root, parent);// 处理profile标签(其作用类比pom.xml中的profile)String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);// 解析预处理,留给子类实现this.preProcessXml(root);// 解析并注册BeanDefinitionthis.parseBeanDefinitions(root, this.delegate);// 解析后处理,留给子类实现this.postProcessXml(root);
}

方法在解析并注册 BeanDefinition 前后各设置一个模板方法,留给子类扩展实现,而在this.parseBeanDefinitions(root, this.delegate)中执行解析和注册逻辑:方法中判断当前标签是默认标签还是自定义标签,并按照不同的策略去解析。

protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){if (delegate.isDefaultNamespace(root)) {// 解析默认标签NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {// 解析默认标签this.parseDefaultElement(ele, delegate);} else {// 解析自定义标签delegate.parseCustomElement(ele);}}}} else {// 解析自定义标签delegate.parseCustomElement(root);}
}

到这里我们已经完成了静态配置到动态 BeanDefinition 的解析,这个时候 bean 的定义已经处于内存中。

【4】 从 IOC容器加载获取 bean:我们可以调用 beanFactory.getBean("myBean") 方法来获取目标对象。

MyBean myBean = (MyBean) beanFactory.getBean("myBean");

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

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

相关文章

【Spring MVC篇】返回响应

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、返回静态页面…

使用Python创建API服务器并打包成exe文件

本文来记录下使用Python创建API服务器并打包成exe文件 文章目录 概述简述API服务器创建打包API服务器为exe文件本文小结 概述 在软件开发中&#xff0c;API服务器是连接前端和后端服务的桥梁&#xff0c;而Python因其丰富的库和框架&#xff0c;如Flask、Django等&#xff0c;成…

MHA切换过程

MHA&#xff08;Master High Availability&#xff09;是一套用于MySQL数据库的高可用性解决方案&#xff0c;它能够在主服务器发生故障时自动将一个从服务器提升为新的主服务器&#xff0c;从而实现数据库服务的持续可用。MHA的切换过程主要包括以下几个步骤&#xff1a; 1. …

NextUI 教程:打造美观高效的React UI

NextUI 教程&#xff1a;打造美观高效的React UI 项目地址:https://gitcode.com/gh_mirrors/ne/nextui 1. 项目介绍 NextUI 是一个轻量级、快速且现代化的React UI库&#xff0c;提供了一系列优雅的组件以帮助开发者构建令人印象深刻的Web应用。它注重性能和用户体验&#x…

Python和Java后端开发技术对比

在当今互联网技术飞速发展的时代&#xff0c;后端开发扮演着至关重要的角色。Python和Java作为两大主流的后端开发语言&#xff0c;各自具备独特的优势和应用场景。让我们深入了解这两种技术的特点和选择建议。 Java后端开发一直是企业级应用的首选方案。它以强大的类型系统、…

Java HashMap

HashMap 是一个散列表&#xff0c;它存储的内容是键值对(key-value)映射。 HashMap 实现了 Map 接口&#xff0c;根据键的 HashCode 值存储数据&#xff0c;具有很快的访问速度&#xff0c;最多允许一条记录的键为 null&#xff0c;不支持线程同步。 HashMap 是无序的&#x…

模型案例:| 帐篷检测模型!

导读 2023年以ChatGPT为代表的大语言模型横空出世&#xff0c;它的出现标志着自然语言处理领域取得了重大突破。它在文本生成、对话系统和语言理解等方面展现出了强大的能力&#xff0c;为人工智能技术的发展开辟了新的可能性。同时&#xff0c;人工智能技术正在进入各种应用领…

实验日志——DETR

DETR训练日志 1. 代码来源 代码源自作者的Github: https://github.com/facebookresearch/detr?tabreadme-ov-file 2. 数据来源 在DETR中只使用了COCO2017数据集&#xff0c;其中训练集有118288张图像&#xff0c;验证集有5001张数据&#xff0c;测试集有40671张数据&#…

18、IO流:

18、IO流&#xff1a; 这一章很枯燥无聊~ 文件&#xff1a; 什么是文件&#xff1a; 文件&#xff0c;对我们并不陌生&#xff0c;文件时保存数据的地方&#xff0c;比如我们经常使用的word文档&#xff0c;txt文档&#xff0c;excel文档…都是文件。它既可以保存一张图片&…

24.两两交换链表中的节点 python

两两交换链表中的节点 题目题目描述示例 1&#xff1a;示例 2&#xff1a;示例 3&#xff1a;提示&#xff1a;题目链接 题解解题思路python实现代码解读提交结果 题目 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须…

解决 git 报错 “fatal: unable to access ‘https://github.com/.../.git‘: Recv failure Connection was rese

目录 前言 方法一&#xff1a;取消代理设置 方法二&#xff1a;设置系统代理&#xff08;推荐&#xff09; 方法三 方法四&#xff1a;不挂梯子时 前言 在使用 Git/Git小乌龟 进行代码管理的过程中&#xff0c;经常会遇到各种各样的问题&#xff0c;其中之一就是在执行 g…

推荐8款自动化软件测试必备工具

在现代软件测试开发领域&#xff0c;自动化测试工具的使用已经变得至关重要。 这些工具不仅提高了测试效率&#xff0c;还确保了软件质量和稳定性。 本文将向您介绍8款自动化软件测试必备工具&#xff0c;它们涵盖了各个层面的测试需求&#xff0c;从而助力测试团队更好地应对…

MySQL聚合函数查询

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

Vue3 完结

组合式API - setup选项 组合式API可理解为一系列函数&#xff0c;通常需要调用这些函数去编写将来的组件逻辑&#xff1b; 而setup为组合式API的入口&#xff08;只有先写了setup才能往里写组合式API的函数&#xff09; setup选项的写法及执行时机 执行时机在beforeCreate之前…

简洁的移动端登录注册界面

非常简洁的登录、注册界面模板&#xff0c;使用uni-app编写&#xff0c;直接复制粘贴即可&#xff0c;无任何引用&#xff0c;全部公开。 废话不多说&#xff0c;代码如下&#xff1a; login.vue文件 <template><view class"content"><view class&quo…

2024NIPS | 在目标引导下利用强化学习范式进行图像冲印调优

文章标题&#xff1a;Goal Conditioned Reinforcement Learning for Photo Finishing Tuning 原文链接&#xff1a;RLPixTuner 本文是上海AI Lab联合香港中文大学&#xff08;薛天帆等人&#xff09;发表在2024NIPS上的论文。 1. Abstract 图像冲印调优旨在自动化对图像冲印管…

【Spring】Cookie与Session

一、Cookie是什么&#xff1f; Cookie的存在主要是为了解决HTTP协议的无状态性问题&#xff0c;即协议本身无法记住用户之前的操作。 “状态” 的含义指的是: 默认情况下 HTTP 协议的客端和服务器之间的这次通信&#xff0c;和下次通信之间没有直接的联系 但是实际开发中&…

【最新】linux安装docker并配置加速源

我这边之前本地创建了个虚拟机&#xff0c;linux系统的&#xff0c;用于部署服务器。有时安装一些常用工具或者中间件&#xff0c;还是用docker安装方便&#xff0c;而且docker还有编排服务等功能&#xff0c;实际使用中还是会省不少事的&#xff0c;这里记录下安装docker的过程…

SpringBoot动态配置Nacos

重要知识点 Nacos属性的简单使用将SpringBoot中的所有配置全部放入到Nacos中开发人创建单独的命名空间,修改互不影响Nacos经常变动的配置抽离到外部文件中 将项目中的所有配置全部放到到 1. 首先引入包 <!-- nacos 接入--><!-- https://mvnrepository.com/artifact…

【每天一篇深度学习论文】轻量化自适应提取模块LAE

目录 论文介绍题目&#xff1a;论文地址&#xff1a; 创新点方法模型总体架构核心模块描述1. 轻量级自适应提取&#xff08;LAE&#xff09;模块&#xff1a;2. 多路径旁路特征匹配&#xff08;MSFM&#xff09;模块&#xff1a;3. RFABlock&#xff08;感受野注意力卷积&#…