【spring】spring bean的生命周期

spring bean的生命周期

文章目录

      • spring bean的生命周期
        • 简介
        • 一、bean的创建阶段
        • 二、bean的初始化阶段
        • 三、bean的销毁阶段
        • 四、spring bean的生命周期总述

简介

本文测试并且介绍了spring中bean的生命周期,如果只想知道结果可以跳到最后一部分直接查看。

一、bean的创建阶段

spring中的bean是何时创建的?

在spring中有一个非常重要的注解,叫做**@Scope**,这个注解是用来控制spring中的bean是否是单例的,一般情况下我们不用添加,默认是单例的即@Scope(“singleton”)。但其实还可以传递其他值让spring中的bean不为单例**@Scope(“prototype”)**。

而spring创建bean的时机就和这个注解有关。

现在我们创建两个类,一个类是TestBean用来当测试bean在构造方法中打印一些东西证明他被执行了。另外一个类是TestFactory这个类实现ApplicationContextAware让他成为自己的测试工厂,要注意这两个类都要交给spring来管理。

TestBean

package com.ez4sterben.spring;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean {public TestBean(){System.out.println("testBean实例化");}
}

TestFactory

package com.ez4sterben.spring;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** 测试工厂** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestFactory implements ApplicationContextAware {public ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

接下来启动spring boot项目。
在这里插入图片描述

可以看到这个类在spring项目运行后就被实例化了,即这个bean随着spring项目启动而被创建,接下来我们对代码进行修改。

TestBean

package com.ez4sterben.spring;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Scope("prototype")
@Component
public class TestBean {public TestBean(){System.out.println("testBean实例化");}
}

TestFactory

package com.ez4sterben.spring;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** 测试工厂** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestFactory implements ApplicationContextAware {public ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@PostConstructpublic void init(){System.out.println("-------------------------测试工厂初始化-------------------------------");TestBean bean = applicationContext.getBean(TestBean.class);}
}

其中TestBean加入了@Scope(“prototype”)这个注解,而TestFactory中加入了一个@PostConstruct的初始化方法,这个注解会让其标记的方法在spring项目启动时默认执行。

方法中我们通过工厂来实例化这个bean,再次启动项目查看输出。

在这里插入图片描述

可以看到testBean的构造函数中的输出内容是在applicationContext调用了getBean方法后才输出的,也就是说在加入@Scope(“prototype”)注解后,bean被创建的时机其实是移动到了工厂执行getBean()之后才会创建,有点懒加载的意思。

但其实单例形式的bean也是可以懒加载的。我们只需要在TestBean上加入注解@Lazy即可。

二、bean的初始化阶段

Spring工厂在创建完对象后,调用方法的初始化方法,完成对应的初始化操作。

这里就有几个问题了:

  1. 这个初始化方法是谁提供的?
  2. 这个初始化方法又是由谁调用的?

其实这个初始化方法是发开者根据自己的业务需求来定义的,而这个初始化方法是由spring的工厂来调用的。

那么开发者又是怎么调用初始化方法的呢?

其实这里只需要让需要初始化的bean来实现InitializingBean这个接口即可,接下来我们来实现一下。

package com.ez4sterben.spring;import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean implements InitializingBean {private String name;public TestBean(){System.out.println("testBean实例化");}@Overridepublic void afterPropertiesSet() throws Exception {this.name = "testBean";System.out.println("afterPropertiesSet: " + this.name);}
}

这里我们去掉@Component之外的注解,这样的话我们的bean创建就是随着项目启动进行的,并且实现了初始化方法,给这个类中的name属性赋值并且打印他,接下来启动项目查看一下输出。

显然,这个输出结果表明,在spring项目启动时,testBean这个bean完成了创建,并且根据我们的初始化需求,完成了初始化方法,而我们是没有调用这个初始化方法的,说明由spring默认的工厂来帮我们执行了这个方法。

但是其实可以发现另外一件事,我们刚才是不是使用过一个注解叫做@PostConstruct,他是不是就是用来完成初始化的?

package com.ez4sterben.spring;import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean {private String name;public TestBean(){System.out.println("testBean实例化");}@PostConstructpublic void init(){this.name = "testBean";System.out.println("init: " + this.name);}
}

那么这两种方式的区别是什么呢?

import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;

其实@PostConstruct是由java本身提供的,如果我们使用这个注解可以脱离spring框架的限制,而实现InitializingBean接口就是把一切交给spring,这里我认为还是使用java提供的比较好一些,就如@Autowired与@Resource一样,脱离框架限制总是会更好一些。

可是如果我两个都选择了会怎么样?

package com.ez4sterben.spring;import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean implements InitializingBean{private String name;public TestBean(){System.out.println("testBean实例化");}@PostConstructpublic void init(){this.name = "testBeanInit";System.out.println("init: " + this.name);}@Overridepublic void afterPropertiesSet() throws Exception {this.name = "testBeanAfterPropertiesSet";System.out.println("afterPropertiesSet: " + this.name);}
}

实际上会先执行@PostConstruct中的内容,然后再执行实现方法中的内容。

在这里插入图片描述

再细致一些观察,这个方法的名字很有意思啊afterPropertiesSet,意思是在属性设置之前,什么意思?我们可以联想我们开发中经常用的,同时也是我们刚才提起的,注入。但注入也不仅仅有@Resource这种注入还有@Value这种的注入。

package com.ez4sterben.spring;import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean implements InitializingBean{@Value("testBeanSet")private String name;public TestBean(){System.out.println("testBean实例化");}@PostConstructpublic void init(){System.out.println(getName());this.name = "testBeanInit";System.out.println("init: " + this.name);}@Overridepublic void afterPropertiesSet() throws Exception {this.name = "testBeanAfterPropertiesSet";System.out.println("afterPropertiesSet: " + this.name);}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

这里通过@Value给name属性注入,并且在init方法中打印getName(),如果打印了testBeanSet,说明在这里其实已经是完成了注入了。
在这里插入图片描述

三、bean的销毁阶段

其实这里和初始化阶段类似,有两种实现方式,一个是实现DisposableBean接口,另一个是使用@PreDestroy注解来标记方法。

package com.ez4sterben.spring;import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean implements InitializingBean, DisposableBean {@Value("testBeanSet")private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public TestBean(){System.out.println("testBean实例化");}@PostConstructpublic void init(){System.out.println(getName());this.name = "testBeanInit";System.out.println("init: " + this.name);}@Overridepublic void afterPropertiesSet() throws Exception {this.name = "testBeanAfterPropertiesSet";System.out.println("afterPropertiesSet: " + this.name);}@Overridepublic void destroy() throws Exception {System.out.println(getName());System.out.println("destroy");}@PreDestroypublic void preDestroy(){System.out.println("PreDestroy");}
}

而这整个阶段是发生在工厂关闭之后。关闭工厂的方法在ClassPathXmlApplicationContext中的close(),一般用于关闭资源等操作,这里我们就不具体测试了,另外这个销毁是只适用于@Scope(“singleton”)的bean而且@PreDestory是在重写的destory()之前执行的(最后一部分会有展示)。

四、spring bean的生命周期总述

其实通过我们上面的测试可以发现,spring的声明周期其实是分为四个阶段的,并不只是三个,Spring IOC 中 Bean 的生命周期大致分为四个阶段:实例化(Instantiation)、属性赋值(Populate)、初始化(Initialization)、销毁(Destruction)。

而其中具体的操作也是很复杂的,并不只是我们测试的这些内容。

这里给大家附上详细的图片(来源java程序员进阶之路)

SpringBean生命周期

我们来完整的测试这一整套生命周期

首先改写TestBean

package com.ez4sterben.spring;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** 测试** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {@Value("testBeanSet")private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public TestBean(){System.out.println("1.testBean实例化");}@PostConstructpublic void init(){this.name = "testBeanInit";System.out.println("6.init: " + this.name);}@Overridepublic void afterPropertiesSet() throws Exception {this.name = "testBeanAfterPropertiesSet";System.out.println("7.afterPropertiesSet: " + this.name);}@Overridepublic void destroy() throws Exception {System.out.println(getName());System.out.println("10.destroy");}@PreDestroypublic void preDestroy(){System.out.println("9.PreDestroy");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("4.setBeanFactory");}@Overridepublic void setBeanName(String s) {System.out.println("2.setName: "+ getName());System.out.println("3.setBeanName");}
}

创建一个TestBeanPostProcessor实现BeanPostProcessor接口

package com.ez4sterben.spring;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;/*** 测试bean后置处理程序** @author ez4sterben* @date 2023/07/27*/
@Component
public class TestBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("8.postProcessAfterInitialization");return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("5.postProcessBeforeInitialization");return bean;}
}

重启项目查看输出,其实配置自己的TestBeanPostProcessor 后可以发现还有好多bean都会完成这个过程

在这里插入图片描述

结束项目,查看输出,我们的bean都被销毁了。

在这里插入图片描述

这样就完成了整个bean的生命周期。

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

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

相关文章

图像滤波器

图像噪声 • 图像噪声是图像在获取或是传输过程中受到随机信号干扰,妨碍人们对图像理解及分析处理 的信号。 • 图像噪声的产生来自图像获取中的环境条件和传感元器件自身的质量,图像在传输过程中产 生图像噪声的主要因素是所用的传输信道受到了噪声…

【技术架构】技术架构的演进

文章目录 前言1.名词解释(常见概念)1.1 应用(Application) / 系统(System)1.2 模块(Module) / 组件(Component)1.3 分布式(Distributed)1.4 集群(…

机器学习 day30(正则化参数λ对模型的影响)

λ对Jcv和Jtrain的影响 假设该模型为四阶多项式当λ很大时,在最小化J的过程中,w会很小且接近0,此时模型f(x)近似于一个常数,所以此时模型欠拟合,Jtrain和Jcv都很大当λ很小时,表示模型几乎没有正则化&…

5.2.tensorRT基础(2)-使用onnx解析器来读取onnx文件(源码编译)

目录 前言1. ONNX解析器2. libnvonnxparser.so3. 源代码编译4. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 t…

Rocky Linux 8.4在Tesla P100服务器里的部署及显卡cudnn安装-极度精简

安装Rocky linux教程 https://developer.aliyun.com/article/1074889 注意事项 Tesla P100服务器,按Delete进入bios,设置Daul模式,第一选项选UEFI hard disk(用驱动盘选这个),usb的就选UEFI usb 安装rocky linux时,这两项默认&…

css中flex后文本溢出的问题

原因: 为了给flex item提供一个合理的默认最小尺寸,flex将flex item的min-width 和 min-height属性设置为了auto flex item的默认设置为: min-width: auto 水平flex布局 min-height:auto 垂直flex布局 解决办法&…

【ICCV2023】Scale-Aware Modulation Meet Transformer

Scale-Aware Modulation Meet Transformer, ICCV2023 论文:https://arxiv.org/abs/2307.08579 代码:https://github.com/AFeng-x/SMT 解读:ICCV2023 | 当尺度感知调制遇上Transformer,会碰撞出怎样的火花&#xff1…

【Nodejs】Node.js简介

1.前言 Node 的重要性已经不言而喻,很多互联网公司都已经有大量的高性能系统运行在 Node 之上。Node 凭借其单线程、异步等举措实现了极高的性能基准。此外,目前最为流行的 Web 开发模式是前后端分离的形式,即前端开发者与后端开发者在自己喜…

Gitlab 合并分支与请求合并

合并分支 方式一:图形界面 使用 GitGUI,右键菜单“GitExt Browse” - 菜单“命令” - 合并分支 方式二:命令行 在项目根目录下打开控制台,注意是本地 dev 与远程 master 的合并 // 1.查看本地分支,确认当前分支是否…

2、HAproxy调度算法

HAProxy的调度算法可以大致分为以下几大类: 静态算法:这类算法的调度策略在配置时就已经确定,并且不会随着负载的变化而改变。常见的静态算法有: Round Robin(轮询) Least Connections(最少连接数) Static-Weight(静态权重) Sourc…

QSlider 样式 Qt15.15.2 圆形滑块

在看文档的时候测试了一下demo,然后发现了一个有意思的东西,自定义滑块为带边框的圆形。 在设置的时候边框总是和预期的有点误差,后来发现了这样一个计算方式可以画一个比较标准的圆。(ABCDEF在下方代码块内) 滑块的…

Kubernetes 之CNI 网络插件大对比

介绍 网络架构是Kubernetes中较为复杂、让很多用户头疼的方面之一。Kubernetes网络模型本身对某些特定的网络功能有一定要求,但在实现方面也具有一定的灵活性。因此,业界已有不少不同的网络方案,来满足特定的环境和要求。 CNI意为容器网络接…

【iOS】—— 持久化

文章目录 数据持久化的目的iOS中数据持久化方案数据持久化方式分类内存缓存磁盘缓存 沙盒机制获取应用程序的沙盒路径沙盒目录的获取方式 持久化数据存储方式XML属性列表Preferences偏好设置(UserDefaults)数据库存储什么是序列化和反序列化,…

Hadoop概念学习(无spring集成)

Hadoop 分布式的文件存储系统 三个核心组件 但是现在已经发展到很多组件的s 或者这个图 官网地址: https://hadoop.apache.org 历史 hadoop历史可以看这个: https://zhuanlan.zhihu.com/p/54994736 优点 高可靠性: Hadoop 底层维护多个数据副本,所…

工程师分享:如何解决传导干扰?

电磁干扰 EMI 中电子设备产生的干扰信号是通过导线或公共电源线进行传输,互相产生干扰称为传导干扰。传导干扰给不少电子工程师带来困惑,如何解决传导干扰? 找对方法,你会发现,传导干扰其实很容易解决,只要…

Jmeter基础篇(17)Jmeter中Stop和X的区别

一、前言 在Apache JMeter中,Stop和X之间存在一些区别。虽然它们都是用于结束测试的不同方法,但它们在实施方式和效果上存在一些差异。 二、Jmeter中的Stop 首先,让我们了解一下Stop。 在JMeter中,Stop是指在测试结束时关闭线…

css实现步骤条中的横线

实现步骤中的横线,我们使用css中的after选择器,content写空,然后给这个范围设定一个绝对定位,相当于和它设置伪类选择的元素的位置,直接看代码: const commonStyle useMemo(() > ({fontSize: 30px}),[]…

前端开发中的微服务架构设计

前端服务化和小程序容器技术为前端应用带来了更好的组织结构、可维护性和可扩展性。这些技术的应用将促进前端开发的创新和发展,使团队能够更好地应对复杂的前端需求和业务挑战。通过将前端视为一个服务化的架构,我们能够构建出更强大、可靠且可持续的前…

windows安装npm, 命令简介

安装步骤 要在Windows上安装npm,按照以下步骤操作: 首先,确保您已经在计算机上安装了Node.js。可以从Node.js官方网站(Node.js)下载并安装Node.js。完成Node.js的安装后,打开命令提示符(Command…

Linux推出Debian 12.1,并进行多方面系统修复

据了解,Debian是最古老的 GNU / Linux 发行版之一,也是许多其他基于 Linux 的操作系统的基础,包括 Ubuntu、Kali、MX 和树莓派 OS 等。 此外,该操作系统以稳定性为重,不追求花哨的新功能,因此新版本的发布…