【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;

    @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的生命周期。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

ELK报错no handler found for uri and method [PUT] 原因

执行后提示no handler found for uri and method post,最新版8.2的问题? 原因: index.mapping.single_type: true在索引上 设置将启用按索引的单一类型行为,该行为将在6.0后强制执行。 原 {type} 要改为 _doc,格式如…

MySQL运维:从全备sql文件中提取指定表的数据并恢复

目录 一、运行环境 二、需求说明 三、思路分析 五、具体方案 六、恢复表数据 一、运行环境 系统:CentOS7.3 数据库:MySQL 8.0.21 二、需求说明 线上有个表的数据被误操作了很多,无法通过bin-log进行具体的恢复。所以当前我们需要从全…

Redission分布式锁详解

前言 ​ 在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。而分布式锁它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。 常见的分…

MFC第二十一天 CS架构多页面开发与数据交互、CImageList图像列表介绍 、CListCtrl-SetItem设置列表项的方法

文章目录 CImageList图像列表介绍CListCtrl图标的原理CListCtrl列表图标设置CListCtrl-SetItem设置列表项的方法 CS架构多页面开发与数据交互添加用户实现向导多页数据交互pch.hCLientXq.h CAppCPage1.hCPage1.cppCPage2.hCPage2.cppCWorkerDlg .hCWorkerDlg.cpp 多页数据修改C…

FRR+VPP

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

Spring Boot 应用程序生命周期扩展点妙用

文章目录 前言1. 应用程序生命周期扩展点2. 使用场景示例2.1 SpringApplicationRunListener2.2 ApplicationEnvironmentPreparedEvent2.3 ApplicationPreparedEvent2.4 ApplicationStartedEvent2.5 ApplicationReadyEvent2.6 ApplicationFailedEvent2.7 ApplicationRunner 3. 参…

Linux查看内存的几种方法

PS的拼接方法 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head 进程的 status 比如说你要查看的进程pid是33123 cat /proc/33123/status VmRSS: 表示占用的物理内存 top PID:进程的ID USER:进程所有者 PR:进程的优先级别&#x…

Vue2基础一、快速入门

零、文章目录 Vue2基础一、快速入门 1、Vue 概念 (1)为什么学 前端必备技能 岗位多,绝大互联网公司都在使用Vue 提高开发效率 高薪必备技能(Vue2Vue3) (2)Vue是什么 **概念:…

ARP协议(地址解析协议)详解

ARP协议(地址解析协议)详解 ARP协议的作用映射方式静态映射动态映射 ARP原理及流程ARP请求ARP响应 ARP协议报文首部 ARP协议的作用 ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网环境中&…

【李宏毅 DLHLP 深度学习人类语言处理 HW1】

李宏毅 DLHLP 深度学习人类语言处理 HW1 相关资料HW1 语音小白在网上没有找到这门课的作业分享,那就记录一下自己的作业吧。 相关资料 课程官网:https://speech.ee.ntu.edu.tw/~hylee/dlhlp/2020-spring.php 作业github代码1:https://githu…

给jupter设置新环境

文章目录 给jupternotebook设置新环境遇到的报错添加路径的方法 给jupternotebook设置新环境 # 先在anaconda界面新建环境 conda env list # 查看conda prompt下的有的环境变量 带星号的是当前活跃的 activate XXXX pip install ipykernel ipython ipython kernel install --u…

【机器学习】西瓜书学习心得及课后习题参考答案—第3章线性模型

过了一遍第三章,大致理解了内容,认识了线性回归模型,对数几率回归模型,线性判别分析方法,以及多分类学习,其中有很多数学推理过程以参考他人现有思想为主,没有亲手去推。 术语学习 线性模型 l…

Ubuntu /dev/loop<0..n>挂载的目录的分析

执行命令df -h lkmaoubuntu:~$ df -h Filesystem Size Used Avail Use% Mounted on udev 1.6G 0 1.6G 0% /dev tmpfs 391M 2.1M 389M 1% /run /dev/sda1 59G 30G 26G 54% / tmpfs 2.0G 0 2.0G 0% /dev/s…

Docker 安全 Docker HTTPS请求过程与配置

Docker 容器安全注意点 尽量别做的事 尽量不用 --privileged 运行容器(授权容器root用户拥有宿主机的root权限) 尽量不用 --network host 运行容器(使用 host 网络模式共享宿主机的网络命名空间) 尽量不在容器中运行 ssh 服务 尽…

十三章:使用图像级监督学习像素级语义关联性的弱监督语义分割

0.摘要 分割标签的不足是野外语义分割的主要障碍之一。为了缓解这个问题,我们提出了一个新颖的框架,根据图像级别的类别标签生成图像的分割标签。在这种弱监督的设置下,已知训练模型更倾向于分割局部有区别的部分,而不是整个物体区…

本地部署 Stable Diffusion XL 1.0 Gradio Demo WebUI

StableDiffusion XL 1.0 Gradio Demo WebUI 0. 先展示几张 StableDiffusion XL 生成的图片1. 什么是 Stable Diffusion XL Gradio Demo WebUI2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 安装 Stable Diffusion XL Gradio Demo WebUI6. 启动 Stable Diffusion XL Gradi…

创建自己的docker python容器环境;支持新增python包并更新容器;离线打包、加载image

1、创建自己的docker python容器环境 参考:https://blog.csdn.net/weixin_42357472/article/details/118991485 首先写Dockfile,注意不要有txt等后缀 Dockfile # 使用 Python 3.9 镜像作为基础 FROM python:3.9# 设置工作目录 WORKDIR /app# 复制当前…

[语义分割] DeepLab v1网络(语义分割、信号下采样、空间上的不敏感性、LargeFOV、膨胀卷积、空洞卷积、MSc、Multi-Scale)

Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs 论文地址:Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs参考源码:https://github.com/TheLegendAli/DeepLab-Context DeepL…

ElementUI tabs标签页样式改造美化

今天针对ElementUI的Tabs标签页进行了样式修改,更改为如下图所属的样子。 在线运行地址:JSRUN项目-ElementUI tabs标签页样式改造 大家如果有需要可以拿来修改使用,下面我也简单的贴上代码,代码没有注释,很抱歉&#x…