Spring系列篇--关于Spring Bean完整的生命周期【附有流程图,超级易懂】

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于Spring的相关操作吧

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一.Spring Bean是单例模式还是多例模式

 二.论证Bean是单例模式

代码论证单例模式的结果

简单JavaBean

spring-context.xml

测试类

结果

多例模式的对比结果

spring-context.xml

结果

三.使用单例模式的好处 

四. Spring Bean完整的生命周期 附流程图详细讲解


通过上两篇的讲解,相信大家已经对Spring十分了解了!!总的来说Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,我们只需要记住这一句话,便可以再脑海中会议起关于IOC和AOP的各个知识点啦!!在讲解Bean的生命周期之前,不妨先来讨论一个至关重要的问题:Bean到底是单例模式还是多例模式?

一.Spring Bean是单例模式还是多例模式

  • Bean 是一个 Java 框架,用于简化 Spring 应用程序中的 Bean 生命周期管理。
  • 在 Bean 中,Bean 实例被设计为单例模式,即在整个应用程序中,只有一个 Bean 实例被创建和共享。这种设计可以确保在应用程序中只有一个 Bean 实例被创建,从而避免了多实例之间的竞争和冲突。
  • 单例模式的设计可以带来很多好处
    • 1. 避免创建多个实例,从而减少了内存占用和资源消耗
    • 2. 确保在整个应用程序中只有一个 Bean 实例被创建,从而避免了多实例之间的竞争和冲突
    • 3. 可以方便地控制 Bean 的生命周期,例如在应用程序启动时创建 Bean 实例,在应用程序关闭时销毁 Bean 实例

通过上文的讲述,我们已经知道了Bean是单例模式,可是我们怎么去论证它说的是正确的呢?

 二.论证Bean是单例模式

  • 单例模式和区别
    • 单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。单例模式可以避免创建多个对象,提高代码的可维护性和可读性。在单例模式中,只有一个类被实例化,其他任何尝试实例化的代码都会返回同一个对象。
    • 多例模式则是一种不推荐的设计模式,它允许创建多个对象,这会导致代码的可维护性和可读性降低。在多例模式中,可以有多个类被实例化,并且这些实例之间没有任何关联。这种设计模式通常被滥用,会导致代码的可维护性变差

上面说了这么多,其实无非就是一句话:单例模式中确保一个类只有一个实例,而多例模式允许创建多个对象 如此而来,我们想要论证Bean究竟是不是单例模式就很好论证了,只需要证明在Bean中一个类只有一个实例就可以了,那么话不多说,我们直接上代码!!

代码论证单例模式的结果

简单JavaBean

package com.wh.beanlife;import java.util.List;public class ParamAction {private int age;private String name;private List<String> hobby;private int num = 1;// private UserBiz userBiz = new UserBizImpl1();public ParamAction() {super();}public ParamAction(int age, String name, List<String> hobby) {super();this.age = age;this.name = name;this.hobby = hobby;}public void execute() {// userBiz.upload();// userBiz = new UserBizImpl2();System.out.println("this.num=" + this.num++);System.out.println(this.name);System.out.println(this.age);System.out.println(this.hobby);}
}

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="paramAction" class="com.wh.beanlife.ParamAction"><constructor-arg name="name" value="昆昆"></constructor-arg><constructor-arg name="age" value="25"></constructor-arg><constructor-arg name="hobby"><list><value>唱</value><value>跳</value><value>RAP</value></list></constructor-arg></bean><bean id="instanceFactory" class="com.wh.beanlife.InstanceFactory"init-method="init" destroy-method="destroy"></bean></beans>

测试类

package com.wh.beanlife;import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;/** spring	bean的生命週期* spring	bean的單例多例*/
public class Demo2 {// 体现单例与多例的区别@Testpublic void test1() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
//		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");// System.out.println(p1==p2);p1.execute();p2.execute();//		单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;applicationContext.close();}// 体现单例与多例的初始化的时间点 instanceFactory@Testpublic void test2() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");}// BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式// 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化@Testpublic void test3() {// ClassPathXmlApplicationContext applicationContext = new// ClassPathXmlApplicationContext("/spring-context.xml");Resource resource = new ClassPathResource("/spring-context.xml");BeanFactory beanFactory = new XmlBeanFactory(resource);
//		InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");}}

结果

 

 那如果是多例模式,会是什么样呢?

多例模式的对比结果

我们只需要修改spring-context.xml就能够看出区别了,在其中添加scope="prototype"就ok了

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="paramAction" class="com.wh.beanlife.ParamAction" scope="prototype"><constructor-arg name="name" value="昆昆"></constructor-arg><constructor-arg name="age" value="25"></constructor-arg><constructor-arg name="hobby"><list><value>唱</value><value>跳</value><value>RAP</value></list></constructor-arg></bean><bean id="instanceFactory" class="com.wh.beanlife.InstanceFactory"init-method="init" destroy-method="destroy"></bean></beans>

结果

三.使用单例模式的好处 

  1. 避免全局变量:全局变量可能导致命名冲突和不可预测的行为。使用单例模式可以避免全局变量,使得代码更加可维护和可测试
  2. 资源共享:如果多个类都使用了相同的资源,如数据库连接、日志记录等,使用单例模式可以避免创建多个实例,从而减少资源消耗
  3. 线程安全:单例模式保证了多线程环境下的线程安全,避免了多个实例同时访问共享资源的情况
  4. 方便管理:单例模式使得多个类可以共享一个实例,从而方便了管理和维护。例如,多个类都可以使用同一个日志记录器或配置管理器,从而减少了代码的耦合性

不妨讲个小例子,让大家更加明白使用单例模式的好处!

话说有一个家庭生了一对双胞胎崽崽,两个小男孩渐渐长大,他们一家一起去逛商场,两个小男孩都说很喜欢一款玩具小汽车,妈妈这时候犯难了,两个小孩都喜欢,可是他们一向都是三分钟热度,妈妈就觉得没必要买两个一样的玩具小汽车,就只买了一台。两个兄弟商量一人上午玩,一人下午玩。这样商量好之后,他们都觉得很不错。可到了真正玩了这个小汽车之后,下午玩这个小汽车的崽崽不开心了,就跑去跟妈妈说,“妈妈,我不想要下午玩这个小汽车,因为哥哥总是把小汽车搞得脏兮兮的😭😭”妈妈没办法,只好又买了一个小汽车给小崽崽,这样之后,两个崽崽都非常的满意的玩起了各自的小汽车!!

在上文的这个故事中,只买一个小汽车就对应着单例模式:确保一个类只有一个实例

而买了两个小汽车则对应着多例模式:允许创建多个对象

如此而来就能够理解它们两者的区别了吧,单例模式容易污染对象,就像文中大崽崽使用玩具车的时候将玩具车弄脏了,以至于小崽崽没有好的体验,但是单例模式可以节约内存,就想只买一个小汽车可以节约钱一样;多例模式不容易污染对象,文中买了两个小汽车就是这样的,两个崽崽都有各自的小汽车,不会出现各自都有不好的体验,但多例模式会比较占用内存,就像买两个小汽车需要多花钱一样

四. Spring Bean完整的生命周期 附流程图详细讲解

 

启动spring,就是创建beanFactory(bean工厂), 一般用的是beanFactory的子类applicationcontext, applicationcontext比一般的beanFactory要多很多功能,比如aop、事件等。  通过applicationcontext加载配置文件,或者利用注解的方式扫描将bean 的配置信息加载到spring容器里面

将加载的配置信息封装成BeanDefinition对象:加载之后,spring容器会将这些配置信息(java bean的信息),封装成BeanDefinition对象,BeanDefinition对象其实就是普通java对象之上再封装一层, 赋予一些spring框架需要用到的属性,比如是否单例,是否懒加载等等

BeanFactory:然后将这些BeanDefinition对象以key为beanName,值为BeanDefinition对象的形式存入到一个map里面,将这个map传入到spring beanfactory去进行springBean的实例化


BeanFactoryPostProcessor:传入到pring beanfactory之后,利用BeanFactoryPostProcessor接口这个扩展点去对BeanDefinition对象进行一些属性修改

Bean实例化:开始循环BeanDefinition对象进行springBean的实例化,springBean的实例化也就是执行bean的构造方法(单例的Bean放入单例池中,但是此刻还未初始化),在执行实例化的前后,可以通过InstantiationAwareBeanPostProcessor扩展点 (作用于所有bean)进行一些修改

Bean属性注入:spring bean实例化之后,就开始注入属性,首先注入自定义的属性,比如标注@autowrite的这些属性,再调用各种Aware接口扩展方法,注入属性(spring特有的属性),比如BeanNameAware.setBeanName,设置Bean的ID或者Name

初始化bean,对各项属性赋初始化值:初始化前后执行BeanPostProcessor(作用于所有bean)扩展点方法,对bean进行修改

      初始化前后除了BeanPostProcessor扩展点还有其他的扩展点,执行顺序如下:

        (1). 初始化前                       postProcessBeforeInitialization()
        (2). 执行构造方法之后                执行 @PostConstruct 的方法
        (3). 所有属性赋初始化值之后           afterPropertiesSet()
        (4). 初始化时                         配置文件中指定的 init-method 方法
        (5). 初始化后                       postProcessAfterInitialization()

        先执行BeanPostProcessor扩展点的前置方法postProcessBeforeInitialization(),
        再执行bean本身的构造方法
        再执行@PostConstruct标注的方法
        所有属性赋值完成之后执行afterPropertiesSet()
        然后执行 配置文件或注解中指定的 init-method 方法
        最后执行BeanPostProcessor扩展点的后置方法postProcessAfterInitialization()

Bean初始化完成:此时已完成bean的初始化,在程序中就可以通过spring容器拿到这些初始化好的bean

Bean随之而销毁: 随着容器销毁,springbean也会销毁,销毁前后也有一系列的扩展点。销毁bean之前,执行@PreDestroy 的方法销毁时,执行配置文件或注解中指定的 destroy-method 方法


结束: 以上就是spring bean的整个生命周期,下面再来个总结!!

          ①根据配置文件或注解信息,生成BeanDefinition,
          ②循环BeanDefinition去实例化

          ③注入属性

          ④初始化

          ⑤销毁,

          在这②③④⑤这几个阶段执行前后,spring框架提供了一系列的扩展点

关于Spring Bean声明周期的讲解,借鉴http://t.csdn.cn/TifUQ了,大家也可以去看看这位大佬写的博文,超赞!

最后再附上一张图(结合上面的文字解释观看会更加爽!!)

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊    

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

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

相关文章

【数据结构】栈和队列常见题目

文章目录 有效的括号用队列实现栈两个队列实现栈一个队列实现栈用栈实现队列设计循环队列最小栈栈的压入&弹出序列逆波兰表达式队列:先进先出 栈:后进先出 有效的括号 https://leetcode.cn/problems/valid-parentheses/ class Solution {public:bool isValid(string s) {…

如何让多线程步调一致?

前几天老板突然匆匆忙忙的过来说对账系统最近越来越慢了&#xff0c;能不能快速优化一下&#xff1f;我了解了对账系统的业务后&#xff0c;发现还是挺简单的&#xff0c;用户通过在线商城下单&#xff0c;会生成电子订单&#xff0c;保存在订单库。之后物流会生成派送单给用户…

Redis - 数据类型映射底层结构

简介 从数据类型上体现就是&#xff0c;同一个数据类型&#xff0c;在不同的情况下会使用不同的编码类型&#xff0c;底层所使用的的数据结构也不相同。 字符串对象 字符串对象的编码可以是 int、raw 和 embstr 三者之一。 embstr 编码是专门用于保存简短字符串的一种优化编…

网络安全--负载均衡

负载均衡 webshell实践 一、负载均衡配置 1.在全局的http下写下它&#xff1a; upstream nginx_boot{# 30s内检查心跳发送两次包&#xff0c;未回复就代表该机器宕机&#xff0c;请求分发权重比为1:2server 192.168.0.000:8080 weight100 max_fails2 fail_timeout30s; ser…

LeetCode150道面试经典题-- 合并两个有序链表(简单)

1.题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 2.示例 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输…

“一日之际在于晨”,欢迎莅临WAVE SUMMIT上午场:Arm 虚拟硬件早餐交流会

8月16日&#xff0c;盛夏的北京将迎来第九届WAVE SUMMIT深度学习开发者大会。在峰会主论坛正式开启前&#xff0c;让我们先用一份精美的元气早餐&#xff0c;和一场“Arm虚拟硬件交流会”&#xff0c;唤醒各位开发小伙伴的开发魂&#xff01; 8月16日&#xff0c;WAVE SUMMIT大…

时序预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络时间序列预测预测效果基本介绍模型描述程序设计学习总结参考资料 预测效果 基本介绍 时序预测 | MATLAB实现WOA-…

R语言APSIM模型进阶应用与参数优化、批量模拟实践技术

随着数字农业和智慧农业的发展&#xff0c;基于过程的农业生产系统模型在模拟作物对气候变化的响应与适应、农田管理优化、作物品种和株型筛选、农田固碳和温室气体排放等领域扮演着越来越重要的作用。APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生…

《论文阅读14》FAST-LIO

一、论文 研究领域&#xff1a;激光雷达惯性测距框架论文&#xff1a;FAST-LIO: A Fast, Robust LiDAR-inertial Odometry Package by Tightly-Coupled Iterated Kalman Filter IEEE Robotics and Automation Letters, 2021 香港大学火星实验室 论文链接论文github 二、论文概…

LeetCode49.字母异味词分组

我一开始的思路就是用1个hashmap<Integer,List<String>>,Integer存的的是字符串所有字母ASCLL值的和&#xff0c;List里面放异位字符串&#xff0c;但是不是异位的字符串的ascll值也可能相同比如acd和abe&#xff0c;所以这个hashmap只能降低一点时间复杂度我还是要…

Vue--》打造个性化医疗服务的医院预约系统(六)

今天开始使用 vue3 + ts 搭建一个医院预约系统的前台页面,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GithHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关…

Web APIs 第六天

正则表达式介绍语法元字符修饰符 一.正则表达式介绍 ① 简介 用来匹配字符串中字符组合的模式在JavaScript中&#xff0c;正则表达式也是对象通常用来查找&#xff0c;替换那些符合正则表达式的文本&#xff0c;许多语言都支持正则表达式 ② 使用场景 验证表单&#xff1a…

算法通关村第4关【白银】| 栈的经典算法问题

1.括号匹配问题 思路&#xff1a;将左括号压入栈中&#xff0c;遍历字符串&#xff0c;当遇到右括号就出栈&#xff0c;判断是否是匹配的一对&#xff0c;不是就返回false&#xff08;因为按照顺序所以当遇到右括号出栈一定要是匹配的&#xff09;。使用Map来简化ifelse clas…

问道管理:放量打拐什么意思?常见的放量打拐三种形态?

成交量一直是股票交易中比较重要的目标&#xff0c;那么&#xff0c;放量打拐是什么意思&#xff1f;常见的放量打拐三种形状是什么&#xff1f;下面问道管理为我们预备了相关内容&#xff0c;以供参阅。 放量打拐什么意思&#xff1f; 放量是指股票成交量与前几个交易日比较显…

安装和配置 Ansible

安装和配置 Ansible 按照下方所述&#xff0c;在控制节点 control.area12.example.com 上安装和配置 Ansible&#xff1a; 安装所需的软件包 创建名为 /home/curtis/ansible/inventory 的静态清单文件&#xff0c;以满足以下要求&#xff1a; node1 是 dev 主机组的成员 node2 …

openGauss学习笔记-43 openGauss 高级数据管理-事件触发器

文章目录 openGauss学习笔记-43 openGauss 高级数据管理-事件触发器43.1 语法格式43.2 参数说明43.3 示例 openGauss学习笔记-43 openGauss 高级数据管理-事件触发器 触发器会在指定的ddl事件发生时自动执行函数。目前事件触发器仅在PG兼容模式下可用。 43.1 语法格式 创建事…

独家!网络机顶盒哪个好?测评员深度对比盘点网络机顶盒排名

网络机顶盒称得上是家家户户必备&#xff0c;每年我都会进行网络机顶盒的测评&#xff0c;今年已经测评过十几款了&#xff0c;后台收到很多私信不知道网络机顶盒哪个好&#xff0c;我本期整理了网络机顶盒排名&#xff0c;大家在选购时可以参考&#xff1a; ◆泰捷WEBOX 60Pro…

Keepalived + Nginx 实现高可用

一、简介 浮动IP、漂移IP地址又叫做VIP&#xff0c;也就是虚拟IP。 Keepalived 是一种高性能的服务器高可用或热备解决方案。 Keepalived 可以用来防止服务器单点故障的发生&#xff0c;通过配合 Nginx 可以实现 web 前端服务的高可用。 Keepalived 以 VRRP 协议为实现基础&a…

【C++】模板template

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ C       &#x1f6f0;️社区 :✈️ 进步学堂       &#x1f6f0;️欢…

Django之定时任务--apscheduler

Django--定时任务apscheduler的使用 apscheduler定时任务的使用1、安装包2、配置settings.py3、在manage.py的文件同级目录下创建文件scheduler.py4、在项目的urls.py中调用这个定时计划5、然后启动项目 python manage.py runserver,在admin中查看就能看到你的定时任务及执行的…