【spring】spring bean的生命周期

news2024/11/16 7:40:42

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;

    @Override
    public 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;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    public 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实例化");
    }

    @Override
    public 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实例化");
    }

    @PostConstruct
    public 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实例化");
    }

    @PostConstruct
    public void init(){
        this.name = "testBeanInit";
        System.out.println("init: " + this.name);
    }

    @Override
    public 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实例化");
    }

    @PostConstruct
    public void init(){
        System.out.println(getName());
        this.name = "testBeanInit";
        System.out.println("init: " + this.name);
    }

    @Override
    public 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实例化");
    }

    @PostConstruct
    public void init(){
        System.out.println(getName());
        this.name = "testBeanInit";
        System.out.println("init: " + this.name);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.name = "testBeanAfterPropertiesSet";
        System.out.println("afterPropertiesSet: " + this.name);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println(getName());
        System.out.println("destroy");
    }

    @PreDestroy
    public 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实例化");
    }

    @PostConstruct
    public void init(){
        this.name = "testBeanInit";
        System.out.println("6.init: " + this.name);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.name = "testBeanAfterPropertiesSet";
        System.out.println("7.afterPropertiesSet: " + this.name);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println(getName());
        System.out.println("10.destroy");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("9.PreDestroy");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4.setBeanFactory");
    }

    @Override
    public 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 {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("8.postProcessAfterInitialization");
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5.postProcessBeforeInitialization");
        return bean;
    }
}

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

在这里插入图片描述

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

在这里插入图片描述

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

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

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

相关文章

创建维基WIKI百科和建立百度百科有何不同?

很多企业有出口业务,想在互联网上开展全球性网络营销,维基百科往往被认为是开展海外营销的第一站。其作用相当于开展国内网络营销的百度百科,经常有些企业给小马识途营销顾问提供的词条内容就是百度百科的内容,可事实上两个平台的…

无人机影像配准并发布(共线方程)

无人机影像 DEM 计算四个角点坐标(刚性变换) 像空间坐标(x,y,-f) 像空间坐标畸变纠正 deltax,deltay 已知(x,y),求解(X,Y, Z)或者(Lat,Lon) 这里的Z是DEM上获取的坐标和Zs为相机坐标的高程,如果均为已…

水文章——推荐一个视频播放器和一个图片查看器

视频播放器——PotPlayer http://www.potplayercn.com/ 图片查看器——JPEGVIEW https://www.bilibili.com/video/BV1ZY411P7fX/?spm_id_from333.337.search-card.all.click&vd_sourceab35b4ab4f3968642ce6c3f773f85138

PHP数组转对象和对象转数组

PHP数组转对象和对象转数组 <?php function array_to_object($arr){$obj new stdClass();foreach ($arr as $key > $val) {if (is_array($val) || is_object($val)) {$obj->$key array_to_object($val);} else {$obj->$key $val;}}return $obj; } function o…

pdf怎么转换成word 文档?这几种方法收藏一下

pdf怎么转换成word 文档&#xff1f;PDF和Word是我们平时工作和学习中最常用的两种文档格式。PDF文档格式通常用于电子书籍、合同、申请表等需要保持原样式的文档。而Word文档则通常用于编辑和修改。但是&#xff0c;有时我们需要将PDF文档转换为可编辑的Word文档&#xff0c;以…

【Docker】在Docker大火的背后,究竟隐藏着未来科技发展的哪些大趋势

这里写目录标题 在docker大火的背后是什么新科技的发展呢&#xff1f;1.容器化技术的普及2.云原生应用的兴起3.边缘计算的发展4.容器编排和管理平台的演进5.混合云和多云架构的普及 docker三大特性轻量化可移植性可扩展性 docker被谁抢了风头呢1.风头被Kubernetes (K8S)抢了2.缺…

日撸代码300行:第54天(基于 M-distance 的推荐)

代码来自闵老师”日撸 Java 三百行&#xff08;51-60天&#xff09;“&#xff0c;链接&#xff1a;日撸 Java 三百行&#xff08;51-60天&#xff0c;kNN 与 NB&#xff09;_闵帆的博客-CSDN博客 算法是基于M-distance的推荐&#xff0c;通过用户评分矩阵对用户进行电影推荐。…

如果你在选型低代码平台,可以从这5个角度去分析抉择

研究低代码平台已有3年&#xff0c;也算是个低代码资深用户了&#xff0c;很多企业面临低代码选型上的困难&#xff0c;选平台容易&#xff0c;换平台难。下面基于个人理解给大家做一份千字的注意事项&#xff01;希望对大家在选型低代码方面有一定帮助。最终&#xff0c;正确且…

[AWD靶场搭建]

文章目录 [AWD靶场搭建]前言AWD平台搭建靶机搭建Cadinal添加靶机 连接Asteroid大屏默认ssh账号密码参考 [AWD靶场搭建] 前言 觉得好玩搭建了一下AWD靶场&#xff0c;使用了vidar-team编写的 Cardinal AWD平台搭建 这里我是在kali搭建的&#xff0c;所以我下载了这个压缩包&…

centos7搭建k8s环境并部署springboot项目

之前看了很多文章&#xff0c;都是部署后一直报错&#xff0c;百度解决后下次又忘了&#xff0c;这次决定把从头到尾的过程记录下来方便下次再看&#xff0c;部署参考文章尚硅谷Kubernetes&#xff08;k8s&#xff09;视频学习笔记_尚硅谷k8s笔记_溯光旅者的博客-CSDN博客 1、…

13年测试老鸟,接口性能测试总结整理,据说这是全网最全的...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试按照不同…

支持多种通信方式和协议方便接入第三方服务器或云平台

2路RS485串口是一种常用的通信接口&#xff0c;可以支持Modbus Slave协议&#xff0c;并可接入SCADA、HMI、DSC、PLC等上位机。它还支持Modbus RTU Master协议&#xff0c;可用于扩展多达48个Modbus Slave设备&#xff0c;如Modbus RTU远程数据采集模块、电表、水表、柴油发电机…

GAN论文精读

标题:Generative Adversarial Nets 摘要: 简写:作者提出了一个framework通过一个对抗的过程&#xff0c;在这里面会同时训练两个模型。 第一个模型为生成模型G&#xff0c;是用来抓住整个数据的分布 第二个模型为辨别模型D&#xff0c;是用来估计一个样本是否从G中产生。 …

BD Biosciences通过使用Liquid UI优化SAP QM,节省了80%的处理时间,提高了 95% 的数据准确性

背景 BD 生物科学公司成立于 1897 年&#xff0c;致力于改善患者的治疗效果&#xff0c;并在一个多世纪的时间里始终坚持这一理念&#xff0c;现已涉足诊断、生物科学以及各种医疗设备和仪器系统。 挑战 手动验证数据 原因&#xff1a;使用非自动程序演示和验证数据&#xff0c…

FRR+VPP

安装 三者的结合&#xff0c;实际上编译安装好就行了&#xff0c;不需要做任何代码上的修改&#xff0c;只需要安装和配置&#xff0c;然后你就有了一台路由器。 FRRouting使用frr-8.5.2版本&#xff0c;VPP使用23.06版本&#xff0c;DPDK和lcpng是VPP的插件&#xff0c;安装…

【CAS6.6源码解析】源码构建时-默认service配置不生效解决方案

CAS6的源码提供了默认的HTTPSandIMAPS-10000001.json配置用于授权所有的https和imaps服务&#xff0c;但是当添加JsonServiceRegistry模块启动后&#xff0c;会发现service是没有被注册的&#xff0c;是由于json路径引起的错误&#xff0c;可以把路径修改为绝对路径以解决此问题…

在idea中添加try/catch的快捷键

在idea中添加try/catch的快捷键 在idea中添加try/catch的快捷键 ctrlaltt 选中想被try/catch包围的语句&#xff0c;同时按下ctrlaltt&#xff0c; 出现下图 选择try/catch即可。

QTDAY3

闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //定时器事件处理函数 #include <QTime> //时间类 #include <QString> #include <QPushButton> #include <QTextToSpeech> #include …

spring中存储和获取bean对象

文章目录 1. 存储bean对象1.1 使用配置文件存储bean对象1.2 使用五大类注解存储bean对象1.2.1 类注解1.2.2 五大类注解的作用1.2.3 方法注解 2.获取bean对象2.1 属性注入2.2 构造器注入2.3 getter注入2.4 注入对象的时候有spring中有多个bean对象怎么办2.4.1 将类中属性的名字&…

ava版知识付费平台免费搭建 Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台

提供私有化部署&#xff0c;免费售后&#xff0c;专业技术指导&#xff0c;支持PC、APP、H5、小程序多终端同步&#xff0c;支持二次开发定制&#xff0c;源码交付。 Java版知识付费-轻松拥有知识付费平台 多种直播形式&#xff0c;全面满足直播场景需求 公开课、小班课、独…