Bean的作用域
前言
Spring 的主要功能就是存储和读取 Bean,因此在 Spring 中 Bean 是最核心的操作资源。那么什么是 Bean 的作用域呢?
一、案例
假设现在有⼀个公共的 Bean,提供给 A 用户和 B 用户使用,然而在使用过程中 A 用户却“悄悄”地修改了公共 Bean 的数据,导致 B 用户在使用时发生了预期之外的逻辑错误。
我们预期的结果是,公共 Bean 可以在各自的类中被修改,但不能影响到其他类。
1.1 被修改的 Bean
User 类:
package yyhgo;
public class User {
int id;
String 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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
存入公共 Bean:
package yyhgo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import yyhgo.User;
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
A 用户使⽤时,进行了修改操作:
package yyhgo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@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 的时候:
package yyhgo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BeanScopesController2 {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
return user;
}
}
打印 A 用户和 B 用户公共 Bean 的值:
package yyhgo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanScopesTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanScopesController beanScopesController = context.getBean(BeanScopesController.class);
System.out.println("A 对象修改之后 Name:" + beanScopesController.getUser1().toString());
BeanScopesController2 beanScopesController2 = context.getBean(BeanScopesController2.class);
System.out.println("B 对象读取到的 Name:" + beanScopesController2.getUser1().toString());
}
}
1.2 原因分析
出现以上问题的原因是 Bean 默认情况下是单例状态(singleton),也就是所有人使用的都是同⼀个对象。之前我们学习单例模式的时候知道:使用单例可以很大程度地提高性能,所以在 Spring 中 Bean 的作用域默认也是 singleton 单例模式。
二、作用域定义
以往程序中变量的可用范围就是作用域,或者说在源代码中定义变量的某个区域就是作用域。
而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。
比如 singleton (单例作用域),就表示 Bean 在整个 Spring 中只有一份,它是全局共享的。那么当一个人修改了这个值之后,其他人读取到的就是被修改的值。
三、Bean 的 6 种作用域
singleton:
官⽅说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean 等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
场景:通常 无状态 的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
备注:Spring 默认选择该作用域
prototype:
官⽅说明:Scopes a single bean definition to any number of object instances.
描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean 等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例。
场景:通常 有状态 的Bean使⽤该作⽤域
request:
官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
描述:每次http请求会创建新的Bean实例,类似于prototype
场景:⼀次http的请求和响应的共享Bean
备注:限定SpringMVC中使⽤
session:
官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在⼀个http session中,定义⼀个Bean实例
场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
备注:限定SpringMVC中使⽤
application(了解):
官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:限定SpringMVC中使⽤
websocket(了解):
官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
备注:限定Spring WebSocket中使⽤
单例作⽤域(singleton) VS 全局作⽤域(application):
- singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
- singleton 作⽤于 IoC 的容器,而 application 作⽤于 Servlet 容器。
四、设置作用域
使用 @Scope 标签就可以设置 Bean 的作用域,如下代码所示:
package yyhgo;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
@Scope 标签既可以修饰方法也可以修饰类!
达到预期。
@Scope 有两种设置方式:
- 直接设置值:@Scope(“prototype”)
- 类似“枚举”设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
(点开源码发现,其实只是 String 变量)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/118555.html