目录
在 Spring 中 Bean 是最核心的操作,接下来我们来分析 Bean 作用域的问题
1.案例
现在有一个公共 Bean(默认是单例模式)的 User 类:
@Component
public class Users {
@Bean(name = "user")
@Scope("prototype") //作用域
public User getUser() {
User user = new User();
user.setId(5);
user.setName("小温");
return user;
}
}
A 用户进行了修改操作(UserController1 类):
@Controller
public class UserController1 {
@Autowired
private User user;
public void doMethod() {
User user2 = user;
System.out.println("UserController1 修改之前:User -> " + user);
user2.setName("三三");
System.out.println("UserController1 修改之后:User -> " + user);
}
}
B 用户使用公共 Bean(UserController2 类):
@Controller
public class UserController2 {
@Autowired
private User user;
public void doMethod() {
System.out.println("UserController2 :User -> " + user);
}
}
打印 A 用户和 B 用户公共 Bean 的值:
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController1 userController1 = context.getBean("userController1", UserController1.class);
userController1.doMethod();
UserController2 userController2 = context.getBean("userController2", UserController2.class);
userController2.doMethod();
}
}
此时我们发现 B 用户打印的是修改后的值:因为 Bean 默认情况下是单例状态(singleton),也就是所有人使用的都是同⼀个对象,之前我们学单例模式的时候都知道,使用单例可以很大程度上提高性能,所以在 Spring 中 Bean 的作用域默认也是 singleton 单例模式。
2.作用域
Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。 比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他人修改了这个值之后,那么另一个人读取到的就是被修改的值。
2.1 Bean 作用域类型
1️⃣singleton 单例模式(默认的作用域)
该作用域下的 Bean 在 IoC 容器中只存在一个实例:获取 Bean(即通过 applicationContext.getBean等方法获取)及装配 Bean 都是同⼀个对象
单例模式的 Bean 是线程安全的吗? 不是(所有人都可以进行操作),使用 ThreaddLocal(本地线程变量)
2️⃣prototype:原型作用域(多例作用域)
每次对该作用域下的 Bean 的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean 等方法获取)及装配 Bean(即通过@Autowired 注入)都是新的对象实例
3️⃣request:请求作用域
每次http请求会创建新的Bean实例,类似于prototype;只适用于 Spring MVC 项目(Spring Web)
4️⃣session:会话作用域
在⼀个http session中,定义⼀个Bean实例;⽤户回话的共享Bean, 比如:记录⼀个⽤户的登陆信息;只适用于 Spring MVC 项目(Spring Web)
5️⃣application:应用作用域
在⼀个http servlet Context中,定义⼀个Bean实例;只适用于 Spring MVC 项目(Spring Web)
6️⃣websocket:websocket 作用域,只适用于 websocket 作用域(了)
2.2 设置作用域
使用 @Scope 标签声明 Bean 的作用域
1️⃣@Scope("prototype")
2️⃣@Scope("ConfigurableBeanFactory.SCOPE_PROTOTYPE")
3.Bean 的生命周期
3.1 Spring 生命周期
- 启动容器
- 读取配置进行 Bean 的实例化
- 将 Bean 加入到容器中
- 装配 Bean 属性(给当前类的属性 DI,进行赋值)
- 使用项目,执行 Spring 的相关业务代码
- 释放 Bean,销毁 Bean
- 关闭容器
3.2 Bean 的生命周期
- 实例化(给 Bean 分配内存空间)
- 设置 Bean 属性(进行依赖注入,将依赖的 Bean 赋值到当前类的属性上)
- Bean 的初始化:1️⃣执行各种通知(如 BeanNameAware的接口方法)2️⃣初始化的前置方法 BeanPostProcessor3️⃣初始化方法 @PostConstruct 4️⃣初始化的后置方法 BeanPostProcessor
- 使用 Bean
- 销毁 Bean :销毁容器的各种方法,如 @PreDestroy、DisposableBean 接口方法、destroy-method。
示例:
public class BeanLifeComponent implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("执行了 BeanNameAware -> " + s);
}
//初始化方法——注解
@PostConstruct
public void doPostConstruct() {
System.out.println("执行了 @PostConstruct");
}
//初始化方法——xml
public void myInit() {
System.out.println("执行了 myinit");
}
@PreDestroy
public void doPreDestroy() {
System.out.println("执行了 @PreDestroy");
}
public void sayHi() {
System.out.println("使用 Bean");
}
}
xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="spring.java.demo"></content:component-scan>
<bean id="mybean" class="BeanLifeComponent" init-method="myInit"></bean>
</beans>
测试类:
public class BeanLifeTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeComponent component = context.getBean("mybean", BeanLifeComponent.class);
component.sayHi();
context.close();
}
}