- 1、Bean作用域问题
- 2、作用域定义
- 2.1、Bean的6种作用域
- singleton 单例模式
- prototype 原型作用域
- request 请求作用域
- session 会话作用域
- application 全局作用域(了解)
- websocket
- 单例作用域 vs 全局作用域
- 2.设置作用域
1、Bean作用域问题
通过一个案例来看Bean的作用域。假设有一个公共的Bean对象,提供给A类和B类使用,然而在A类使用的过程中,修改了Bean的数据,导致B类在使用Bean的时候产生了预期之外的逻辑错误。
预期结果是公共Bean可以在各自类中被修改,但不能影响其他类使用(使用时还是Bean的初始数据)。
公共Bean:
@Component
public class Users {
@Bean
public User user1(){
User user = new User();
user.setName("zhangsan");//注意 初始化为 zhangsan
user.setId(1);
return user;
}
}
User对象:
public class User {
private int id;
private 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 + '\'' +
'}';
}
}
A类使用时,修改了数据:把name修改成了Lisi
@Controller
public class AController {
@Autowired
private User user;
public User getUser1(){
System.out.println("Bean 原 name :"+user.getName());
user.setName("Lisi");//A类修改了Bean原生数据
return user;
}
}
B类再去使用公共Bean时:
@Controller
public class BController {
@Autowired
private User user1;
public User getUser1(){
User user = user1;
return user;
}
}
打印A和B类使用公共Bean 的值:
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.config.xml");
AController aController = context.getBean(AController.class);
System.out.println("A类修改后 name :"+aController.getUser1().toString());
BController bController = context.getBean(BController.class);
System.out.println("B读取到的 name :"+bController.getUser1().toString());
}
}
运行结果:
原因分析:
以上运行结果的原因是:因为Bean默认情况下是单例模式(singleton),也就是所有人使用的都是同一个Bean对象。因为使用单例模式可以很大程度上提高性能,所以在spring中Bean的作用域默认也是singleton单例模式。
2、作用域定义
程序中变量的可用范围就叫做作用域,或者说在源代码中定义某个变量在某个区域就叫做作用域。而Bean的作用域是指Bean在Spring整个框架中的某种行为模式,比如singleton单例作用域,就表示Bean在spring中只有一份,全局共享的,当A修改了这个值后,那么B再使用时就是修改后的值。
2.1、Bean的6种作用域
Spring容器在初始化一个Bean对象时,会指定该实例的作用域。spring有6种作用域,最后4种是基于Spring MVC生效的:
1. singleton:单例模式
2. prototype:原型作用域(多例作用域)
3. request:请求作用域
4. session:会话作用域
5. application:全局作用域
6. websocket:HTTP WebSocket 作用域
注意后4种是SpringMVC中的值,在普通的Spring项目中只有前两种。
singleton 单例模式
- 该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
- 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新。
- 备注:Spring默认选择该作⽤域。
prototype 原型作用域
- 描述:每次对该作用域下的Bean的请求都会创建一个新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同新的对象。
- 场景:通常有状态的Bean使用该作用域。
request 请求作用域
- 描述:每次http请求会创建新的Bean实例,类似于prototype
- 场景:⼀次http的请求和响应的共享Bean
- ⼀次http的请求和响应的共享Bean
session 会话作用域
-
描述:在⼀个http session中,定义⼀个Bean实例
-
场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
备。
-
备注:限定SpringMVC中使⽤
application 全局作用域(了解)
- 描述:在⼀个http servlet Context中,定义⼀个Bean实例。
- 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息。
- 备注:限定SpringMVC中使⽤。
websocket
- 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例。
- 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,知道WebSocket结束都是同一个Bean。
- 备注:限定Spring WebSocket中使⽤。
单例作用域 vs 全局作用域
- singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
- singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。
2.设置作用域
@Scope既可以修饰方法也可以修饰类
- 使用@Scope标签设置Bean的作用域: 设置为 prototype 全局作用域
@Component
public class Users {
@Scope("prototype")//设置为 prototype 全局作用域
@Bean
public User user1(){
User user = new User();
user.setName("zhangsan");//注意 初始化为 zhangsan
user.setId(1);
return user;
}
}
运行结果:此时A修改数据,不会影响B读取的数据。
- 使用枚举设置@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class Users {
//@Scope("prototype")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User user1(){
User user = new User();
user.setName("zhangsan");//注意 初始化为 zhangsan
user.setId(1);
return user;
}
}
运行结果: