Bean作用域问题引入
假设有一个Bean对象,但是A用户在使用的时候将Bean对象的数据修改了,导致B用户在使用的时候发生了预期之外的逻辑错误
有一个对象为Animal
public class Animal {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
将该对象注入到Spring容器中
@Component
public class Animals {
@Bean
public Animal animal(){
Animal animal = new Animal();
animal.setName("老虎");
return animal;
}
}
A用户使用Bean对象时,进行了修改操作
@Controller
public class AnimalController1 {
@Autowired
private Animal animal;
public void getAnimal(){
Animal animal = this.animal;
animal.setName("大象");
System.out.println(animal);
}
}
B用户在去使用Bean对象
@Controller
public class AnimalController2 {
@Autowired
private Animal animal;
public void getAnimal(){
System.out.println(animal);
}
}
打印A用户和B用户公共Bean的值
public class App4 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
AnimalController1 animalController1 = context.getBean(AnimalController1.class);
animalController1.getAnimal();
System.out.println("---------");
AnimalController2 animalController2 = context.getBean(AnimalController2.class);
animalController2.getAnimal();
}
}
打印结果:都打印了A用户修改后的值
出现上述结果的原因是Bean默认是单例作用域
,也就是所有用户使用的都是同一个对象,但是我们想要的结果是公共Bean可以被用户在自己的类中修改,但是不能影响到其他类,那该如何做?
了解了下面Bean的作用域后,答案自然知晓
Bean的作用域
Bean的作用域指的是Bean在整个Spring框架中的某个行为模式,比如singleton单例作用域表示Bean在整个Spring中只有一份,是全局共享的,当有一个用户修改了这个对象后,其他用户获取的就是这个修改后的对象
Bean的作用域有六种:
- singleton:单例作用域
- prototype:原型作用域(多例作用域)
- request:请求作用域
- session:会话作用域
- application:全局作用域
- websocket:HTTP WebSocket作用域
注意: 后四种是基于Spring MVC生效的,普通的Spring项目中只有前两种
singleton
该作用域下的Bean在IoC容器中只存在一个实例,通常无状态的Bean使用该作用域,无状态表示Bean对象无人修改,该作用域是Spring默认作用域
,这就是上述问题原因的所在
prototype
每次对该作用域下的Bean的请求都会创建新的实例,通常有状态的Bean使用该作用域,有状态表示Bean对象有人修该,对于上述问题,就可以将Bean的作用域设置为prototype
request
每次http请求会创建新的Bean实例,一次HTTP的请求和响应共享一个Bean对象实例,限定SpringMVC中使用
session
每个HTTP会话中,都会创建Bean实例,用户会话共享Bean, 比如记录一个用户的登陆信息,限定SpringMVC中使用
application
在一个HTTP Servlet上下文中,定义一个Bean实例,Web应用的上下文信息,比如,记录一个应用的共享信息,限定SpringMVC中使用
websocket
在一个HTTP WebSocket的生命周期中,定义一个Bean实例,WebSocket的每次会话中,保存了一个Map结构的头信息,用来包裹客户端消息头,第一次初始化后,直到WebSocket结束都是同一个Bean
限定Spring WebSocket中使用
解决上述问题
通过了解Bean的作用域,可以得到Bean的作用域为prototype,来解决上述问题
Bean作用域的设置方式:
- @Scope(“prototype”)
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
在存储Bean对象的时候添加上述注解,两种方式任选择一种
对上述代码进行修改:只需要在存储Bean时,添加上述两个注解中的一个即可
@Component
public class Animals {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Animal animal(){
Animal animal = new Animal();
animal.setName("老虎");
return animal;
}
}
Bean的生命周期
Bean的生命周期就是一个Bean对象从诞生到销毁的过程
Bean的生命周期分为以下五个步骤:
- 实例化Bean
为Bean分配内存空间
- 设置属性
Bean注入和装配
- 初始化Bean
执行各种Aware通知,如 BeanNameAware,BeanFactoryAware、ApplicationContextAware 的接口方法
执行初始化的前置方法
执行构造方法,两种执行方式,一种是@PostConstruct,另一种是自己指定的init-method方法
执行初始化的后置方法
- 使用Bean
- 销毁Bean
销毁Bean的各种方法,@PreDestroy,DisposableBean接口方法,指定的destroy-method方法
流程如下图: