【JavaEE Spring】Bean的作用域与生命周期详解

15 篇文章 1 订阅
14 篇文章 0 订阅

因为spring其实上是一个用来存储和读取Bean的IOC容器,因此在spring中bean是最核心的操作资源,深入理解bean对象是很重要的。

1. 案列引入bean的作用域问题

通过⼀个案例来看 Bean 作⽤域的问题:
假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,然⽽在使⽤的途中 A ⽤户却“悄悄”地修改了公共 Bean 的数据,导致 B ⽤户在使⽤时发⽣了预期之外的逻辑错误。(预期的结果是,公共 Bean 可以在各⾃的类中被修改,但不能影响到其他类)

公共 Bean:

@Component
public class Users {
@Bean
	public User user1() {
		User user = new User();
		user.setId(1);
		user.setName("Java"); // 【重点:名称是 Java】
		return user;
	}
}

A ⽤户使⽤时,进⾏了修改操作:


```java
	@Controller
	public class BeanScopesController {
	@Autowired
	private User user1;
	public User getUser1() {
		User user = user1;
		System.out.println("Bean 原 Name:" + user.getName());
		user.setName("悟空"); // 【重点:进⾏了修改操作】
		return user;
	}
}

B ⽤户再去使⽤公共 Bean 的时候:

@Controller
	public class BeanScopesController2 {
@Autowired
	private User user1;
	public User getUser1() {
		User user = user1;
		return user;
	}
}

打印 A ⽤户和 B ⽤户公共 Bean 的值:
在这里插入图片描述
在这里插入图片描述
可以看到,bean原来的name是Java,但是被A修改以后,B获取到的公共对象bean的name也变成了修改后的“悟空”

原因分析:
操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中Bean 的作⽤域默认也是 singleton 单例模式。

2. 作用域定义(bean的作用域)

作用域定义:限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。

⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值。

3. Bean 的 6 种作⽤域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,
最后四种是基于 Spring MVC ⽣效的,在普通的 Spring 项⽬中只有前两种:

  1. singleton:单例作⽤域
  2. prototype:原型作⽤域(多例作⽤域)
  3. request:请求作⽤域
  4. session:回话作⽤域
  5. application:全局作⽤域
  6. websocket:HTTP WebSocket 作⽤域

3.1 singleton

  • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通@Autowired注⼊)都是同⼀个对象。
  • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
  • 备注:Spring默认选择该作⽤域

3.2 prototype

  • 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通@Autowired注⼊)都是新的对象实例。
  • 场景:通常有状态的Bean使⽤该作⽤域

3.3 request

  • 描述:每次http请求会创建新的Bean实例,类似于prototype
  • 场景:⼀次http的请求和响应的共享Bean
  • 备注:限定SpringMVC中使⽤

3.4 session

  • 描述:在⼀个http session中,定义⼀个Bean实例
  • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
  • 备注:限定SpringMVC中使⽤

3.5 application(了解)

  • 描述:在⼀个http servlet Context中,定义⼀个Bean实例
  • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
  • 备注:限定SpringMVC中使⽤

3.6 websocket(了解)

  • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
  • 备注:限定Spring WebSocket中使⽤

3.7 单例作⽤域(singleton)和全局作⽤域(application)区别

  • singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
  • singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

4. Bean原理分析

本节总结 Bean 执⾏原理和⽣命周期总结。

4.1 Bean执行流程

在这里插入图片描述

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

4.2 Bean ⽣命周期

⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。

Bean 的⽣命周期分为以下 5 ⼤部分:

  1. 实例化 Bean(为 Bean 分配内存空间)
  2. 设置属性(Bean 注⼊和装配)
  3. Bean 初始化
  • 实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、
  • ApplicationContextAware 的接⼝⽅法;
  • 执⾏ BeanPostProcessor 初始化前置⽅法;
  • 执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;(spring实现)
  • 执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);(xml实现)
  • 执⾏ BeanPostProcessor 初始化后置⽅法。
  1. 使⽤ Bean
  2. 销毁 Bean
    销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method。执⾏流程如下图所示:
    在这里插入图片描述
    实例化和初始化的区别:
    实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;⽽初始化是给开发者提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理。

注意一点:要先设置属性在进⾏初始化呢;因为先设置了属性,在初始化过程中执行构造方式时候就可以调用设置的这个属性,要是没设置属性就在构造方法中调用该属性,就会报 空指针异常 !

所以设置属性和调用构造方法顺序不能颠倒

5. 设置bean的生命周期

使用注解 @Scope 标签设置,即可以修饰方法也可以修饰类, @Scope有两种设置方式:

  1. 直接设置值:如 @Scope(“prototype”)
    在这里插入图片描述

  2. 使用枚举设置:@Scope()
    在这里插入图片描述
    在这里插入图片描述

  • over ~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值