Spring Bean的作用域和生命周期

文章目录

  • 1. Bean的作用域
  • 2. Spring的生命周期
  • 3. Bean的生命周期
  • 4. 相关注解总结

1. Bean的作用域

Bean 的作用域指的是 Bean 在 Spring 容器中的行为(Bean 实例创建及生命周期),它的行为是由 Spring 来管理的,可以根据具体情况选择不同的作用域来达到性能优化、资源利用最大化和可维护性等目的。

Bean 作用域(Scope)类型主要有如下几种:

其中前两种是 Spring 核心作用域,而后 4 种是 Spring MVC 中的作⽤域;

  1. singleton:单例模式(默认的作用域),Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象,通常无状态的 Bean 使用该模式(无状态表示Bean对象的属性不需要更新),这也是 Spring 出于性能方面的考虑。
  2. prototype:原型模式(多例作用域),每次从 Spring 容器中获取 prototype 模式的 Bean 时,容器都将创建一个新的 Bean 实例,每个Bean实例都有自己的属性和状态,而 singleton 全局只有一个对象,通常有状态的 Bean 使用该作用域,即Bean的属性可能会更新。
  3. request:请求模式,在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例,在这次的请求和响应中共享这个 Bean,但在不同的请求中会创建新的实例,也就是说每次 HTTP 请求,使用的是不同的Bean, 类似于 prototype,在 Spring MVC 中使用,即使用需要在 WebApplicationContext 环境下。
  4. session:会话模式,在同一个 HTTP Session 中,共享使用的是一个 Bean 实例,例如记录用户的登录信息,在 Spring MVC 中使用。
  5. application:全局模式,表示整个 Web 应用的 http servlet context 共享同一个 Bean,主要记录的是的 Web 应用的上下文信息,例如记录一个应用的共享信息,在 Spring MVC 中使用。
  6. websocket:网络长连接,只适用于 Spring WebSocket 项目,在一个 HTTP WebSocket 的生命周期中(一次长连接),共享一个 Bean 实例;WebSocket 的每次会话中,保存了一个 Map 结构的头信息,用来保存客户端消息头,第一次初始化后,直到一次长连接结束都是用一个 Bean。

这里就简单用singletonprototype这两种可以在 Spring 核心代码中使用的模式来演示区别一下。

package com.tr.demo.model;public class User {private int id;private String name;@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

一般有两种方式设置 Spring Bean 作用域:

  1. 使用xml配置的方式,我们将bean标签当中的scope属性设置为singleton就为单例,设置为prototype就为多例模式。img
  2. 二是直接通过@Scope注解,不设置参数或设置参数为singleton就为单例,设置为prototype就为多例模式。img
    我们还可在 Scope 注解中可以传入另一种参数形式,和上面直接拼写出来效果一样。
    img
    这种方式是 ConfigurableBeanFactory 类中为我们提供了静态属性,相较于自己拼写不容易出错。img

我们来看一下 singleton 和 prototype 两种模式下获取的 Bean 有什么不同,我们这里演示在这两种不同的模式下将 User 对象放到 Spring 容器中,从容器中先后两次取出 User,看这两次取出的 Bean 是否相同。

public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");User user1 = context.getBean("user", User.class);User user2 = context.getBean("user",User.class);System.out.println(user1 == user2);
}

singleton 模式下的结果:

可以看到,此时 User 类在 Ioc 容器中只创建了一个实例,而通过 applicationContext.getBean() 方法多次获取到的 Bean,都是同一个对象实例,相当于是一个浅拷贝。

img
prototype 模式下的结果:

这里之所以这里打印出来的 Bean 是 2 个而不是 3 个,是因为prototype模式下在我们创建容器时并不会进行 Bean 的实例化,它在我们获取 Bean 的时候才会去创建 1 个实例,而且每次获取 Bean 时都会创建新的实例,它们彼此之间都是不同的实例,相当于是一个深拷贝。

img

2. Spring的生命周期

Spring 的生命周期大致走的的是如下流程

  1. 首先启动容器,加载配置文件(类加载路径下的 spring-config.xml 文件)。
    img
  2. 根据配置文件完成 Bean 的实例化(分配内存空间)和初始化(初始化空间),包括扫描配置文件下带有五大类或者方法注解的类和 bean 标签中要注册的对象,配置文件中的 Bean 是按顺序实例化和初始化的。
    img
  3. 将 Bean 注册到 Spring 容器中
  4. 装配 Bean 的属性,如果 Bean 对象需要其他的 Bean 作为属性,可以使用注解的方式注入(@Autowired、Resource)
    img

小结,Bean执行流程(Spring执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从无到有)-> Bean 注册到 Spring 容器中(存操作) -> 将 Bean 装配到需要的类中(取操作)。

3. Bean的生命周期

Bean 的生命周期,就是 Bean 对象从诞生到销毁的整个过程,Bean 的生命周期大致有 5 个阶段:

①Bean 的实例化(分配内存空间)。

②设置 Bean 属性(进行依赖注入,将依赖的 Bean 辅助到当前类的属性上)。

③Bean 的初始化阶段:

  • 执行各种通知,底层实现了 Aware 接口,执行其中的方法,如:BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
  • 执行初始化的前置方法,即执行 BeanPostProcessor 方法(before);
  • 执行初始化方法 ,依赖注⼊操作之后被执⾏(两种重写方式:xml方式,注解方式@PostConstruct);
  • 执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
  • 执行 BeanPostProcessor 初始化后置方法(after)。

④使用 Bean。

⑤销毁 Bean,执行 销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method。

要注意设置属性必须在初始化之前,因为有可能在初始化的时候使用Bean。

上述流程可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:

  1. 先买房(毛坯房),对应 Bean 的实例化;
  2. 装修,对应给 Bean 设置属性;
  3. 买家电,如洗⾐机、冰箱、电视、空调等,对应 Bean 的各种初始化
  4. ⼊住,对应使⽤ Bean;
  5. 卖房,对应 Bean 的销毁。

执行流程如下图所示:

img

还需要注意区分一下实例化和初始化,实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;⽽初始化是给开发者 提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理。
下面就简单的写几个周期方法来演示一下这个过程,在类中添加两个方法,一个是init方法表示初始化,另外一个是destroy方法,表示Bean销毁前执行的事情,使用@PostConstruct注解或者xmlbean标签中的init-method属性表示在构造后执行,@PreDestroy注解或者xmlbean标签中的destroy-method表示在 Bean 销毁前做的事情。

import com.tr.demo.model.User;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;public class BeanLifeComponent implements BeanNameAware {@Autowiredprivate User user;@Overridepublic void setBeanName(String s) {System.out.println("执行了 BeanNameAware 通知 -> " + s);}@PostConstructpublic void doPostConstruct() {System.out.println("执行了 @PostConstruct (注解式初始化)");System.out.println(user.toString());}public void myInit(){System.out.println("执行了 myinit (xml 式初始化)");}@PreDestroypublic void doPreDestroy(){System.out.println("执行了 @PreDestroy (销毁 Bean 前执行)");}public void myDestroy() {System.out.println("执行了 myDestroy (销毁 Bean 前执行)");}public void sayHi(){System.out.println("使用 Bean");}
}

Spring 配置文件,将 Bean 手动存储在 Spring 容器中,要注意init-methoddestroy-method这两个属性的值要和上面实现的方法名对应。

<bean id="mybean" class="BeanLifeComponent"init-method="myInit" destroy-method="myDestroy">
</bean>

测试代码:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class BeanLifeTest {public static void main(String[] args) {// 父类 ApplicationContext 中没有 close, destroy 系列方法ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanLifeComponent component =context.getBean("mybean",BeanLifeComponent.class);component.sayHi();// 关闭容器context.close();}
}

运行结果:

img如果代码中飘红说 @PostConstruct 和 @PreDestroy 注解如果找不到,需要导入下面的jar包。

<dependency>  
<groupId>javax.annotation</groupId>  
<artifactId>javax.annotation-api</artifactId>  
<version>1.3.2</version></dependency>

找不到的原因是,从 JDK9 以后 jdk 中的 javax.annotation 包被移除了,这两个注解刚好就在这个包中。

4. 相关注解总结

@Scope

名称@Scope
类型类注解
位置类定义上方
作用设置该类创建对象的作用范围 可用于设置创建出的bean是否为单例对象
属性value(默认):定义bean作用范围, 默认值singleton(单例),可选值prototype(非单例)

@PostConstruct

名称@PostConstruct
类型方法注解
位置方法上
作用设置该方法为初始化方法
属性

@PreDestroy

名称@PreDestroy
类型方法注解
位置方法上
作用设置该方法为销毁方法
属性

注解功能与 XML 实现对应关系:

img

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

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

相关文章

Kali Linux助您网络安全攻防实战

Kali Linux&#xff1a;黑客与防御者的神器 Kali Linux是一款专为网络安全测试和攻防实践而设计的操作系统。它汇集了大量的安全工具&#xff0c;可以用于渗透测试、漏洞扫描、密码破解等任务&#xff0c;不仅为黑客提供了强大的攻击能力&#xff0c;也为安全防御者提供了测试和…

【奥义】如何用ChatGPT写论文搞模型

目录 你是否曾经在复现科研论文的结果时感到困难重重&#xff1f; 引言 1 打开需要复现的目标文献 2 提取公式定义的语句 3 文章公式、图实现 &#xff08;1&#xff09;用python复现目标文献中的公式 &#xff08;2&#xff09;用python复现目标文献中的图 4 Copy代码…

重试框架入门:Spring-RetryGuava-Retry

前言 在日常工作中&#xff0c;随着业务日渐庞大&#xff0c;不可避免的涉及到调用远程服务&#xff0c;但是远程服务的健壮性和网络稳定性都是不可控因素&#xff0c;因此&#xff0c;我们需要考虑合适的重试机制去处理这些问题&#xff0c;最基础的方式就是手动重试&#xf…

威海--游记

威海盛夏已至&#xff0c;气温攀升的同时&#xff0c;小伙伴们出去玩的心也都藏不住了。 作为离韩国最近的城市&#xff0c;不出国门就能轻松get到浓浓的“韩范儿”&#xff01;从韩式建筑、小吃甜品&#xff0c;再到各种宝藏打卡小店&#xff0c;玩法超多&#xff0c;好吃好看…

vmagent获取node-exporter配置

vmagent 使用以下命令添加图表 helm 存储库&#xff1a; helm repo add vm https://victoriametrics.github.io/helm-charts/helm repo update 列出vm/victoria-metrics-agent可供安装的图表版本&#xff1a; helm search repo vm/victoria-metrics-agent -l victoria-met…

2023年国赛数学建模思路 - 案例:异常检测

文章目录 赛题思路一、简介 -- 关于异常检测异常检测监督学习 二、异常检测算法2. 箱线图分析3. 基于距离/密度4. 基于划分思想 建模资料 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 一、简介 – 关于异常…

win11如何去掉桌面快捷方式的小箭头(原创)

begin 打开注册表,Windows搜索框里搜 注册表编辑器(register editor),打开.. 找到 接着跟进.. 新建一个项名, Shell Icons 继续.... 值设为29 双击开页面 输入数据 %windir%\System32\shell32.dll,-51 到此,保存,到桌面,小箭头还是没有消失 ctrl shift esc 打开 任务管理…

Redis辅助功能

一、Redis队列 1.1、订阅 subscribe ch1 ch2 1.2 publish:发布消息 publish channel message 1.3 unsubscribe: 退订 channel 1.4 模式匹配 psubscribe ch* 模糊发布&#xff0c;订阅&#xff0c;退订&#xff0c; p* <channelName> 1.5 发布订阅原理 订阅某个频道或…

产品需求管理软件:了解常见选择和功能

产品需求管理软件是一种非常重要的工具。它可以帮助企业更好地理解客户需求&#xff0c;提高产品开发效率并降低成本。本文将介绍一些常见的产品需求管理软件及其主要功能。 “产品需求管理软件有哪些&#xff1f;比较流行的有Zoho Projects、Trello、Asana、Smartsheet等。” …

CHATGPT源码简介与使用指南

CHATGPT源码的基本介绍 CHATGPT源码备受关注&#xff0c;它是一款基于人工智能的聊天机器人&#xff0c;旨在帮助开发者快速搭建自己的聊天机器人&#xff0c;无需编写代码。下面是对CHATGPT搭建源码的详细介绍。 CHATGPT源码的构建和功能 CHATGPT源码是基于Google的自然语言…

解决selenium的“can‘t access dead object”错误

目录 问题描述 原因 解决方法 示例代码 资料获取方法 问题描述 在python执行过程中&#xff0c;提示selenium.common.exceptions.WebDriverException: Message: TypeError: cant access dead object 原因 原因是代码中用到了frame,获取元素前需要切换到frame才能定位到…

ssh-keygen 做好免密登录后不生效

免密说明 通常情况下&#xff0c;我们ssh到其他服务器需要知道服务器的用户名和密码。对于需要经常登录的服务器每次都输入密码比较麻烦&#xff0c;因此我们可以在两台服务器上做免密登录&#xff0c;即在A服务器可以免密登录B服务器。 在A服务器上登录B服务器时&#xff0c;…