Spring 中 Bean 的作用域与生命周期
1. Bean 的作用域
1.1 什么是 Bean 的作用域?
Bean
的作用域是指 Bean
在 Spring
整个框架中的某种行为模式,Bean
的作用域用于定义 在IoC
容器中创建的Bean
实例的生命周期范围。
案例:我们创建如下的类

User: @Getter
、@Setter
、@ToString
是 Lombok
插件以及依赖提供,我们就不用写get
、set
、toSpring
方法了(怎么配置,后文介绍)。
@Getter
@Setter
@ToString
public class User {
private int id;
private String name;
}
UserBeans:
@Component
public class UserBeans {
//创建一个Bean,并把它放入Spring容器中。
@Bean
public User user(){
User user = new User();
user.setId(1);
user.setName("小明");
return user;
}
}
UserController:
@Controller
public class UserController {
@Autowired
private User user;//注入User,此时 User 是“小明”
public void print(){
System.out.println("user--> " + user);
//修改 myUser
User myUser = user;
myUser.setName("张三");
System.out.println("myUser--> " + myUser);
System.out.println("user--> " + user);
}
}
UserController2:
@Controller
public class UserController2 {
@Autowired
private User user;//注入
public void print(){
System.out.println("user--> "+user);//目的:想得到“小明”
}
}
Main:
public class Main {
public static void main(String[] args) {
//获取Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean("userController",UserController.class);
userController.print();
System.out.println("------------------------------------------------------------------------");
UserController2 userController2 = context.getBean("userController2", UserController2.class);
userController2.print();
}
}
结果:
可以看到,User
在全局下是同一个对象,换言之,此处的Bean
在Spring
容器中只有一份。在任何一个地方修改此处的Bean
,它的值就会改变;但是我们预期的结果是,公共 Bean
可以在各自的类中被修改,但不能影响到其他类,显然上面的代码违背了我们的预期,这就是作用域的体现。
1.1.1 Lombok 的配置
第一步:将下面的代码赋值到pom.xml文件中
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>

第二步:在插件商店中下载“Lombok”
第三步:加注解
注解名称 | 作用 |
---|---|
@Getter | 自动生成 getter 方法 |
@Setter | 自动生成 setter 方法 |
@ToString | 自动生成 toString() 方法 |
@EqualsAndHashCode | 自动生成 equals() 和 hashCode() 方法 |
@NoArgsConstructor | 自动生成无参构造函数 |
@AllArgsConstructor | 自动生成包含所有参数的构造函数 |
@RequiredArgsConstructor | 自动生成包含必需参数的构造函数 |
@Data | 自动生成 toString() 、equals() 、hashCode() 、getter 和 setter 方法 |
1.2 Bean 的 6 种作用域
-
Singleton:
单例作用域(默认),整个应用中只有一个 Bean 实例,每次请求时都返回同一个实例。 -
Prototype:
原型作用域,每次请求时都会创建一个新的 Bean 实例,即每次使用该 Bean 时,Spring 都会创建一个新的实例。 -
Request:
请求作用域,每次 HTTP 请求都会创建一个新的 Bean 实例,该作用域仅适用于 WebApplicationContext 环境。 -
Session:
会话作用域,每个 HTTP Session 都会创建一个新的 Bean 实例,该作用域仅适用于 WebApplicationContext 环境。 -
Application:
应用作用域,整个应用程序中只有一个 Bean 实例,该作用域仅适用于 ServletContext 环境。 -
WebSocket:
WebSocket 作用域,每个 WebSocket 连接都会创建一个新的 Bean 实例,该作用域仅适用于 WebApplicationContext 环境。
需要注意的是,Request
、Session
和 WebSocket
这三种作用域只适用于 WebApplicationContext(Spring MVC)
环境,即只能在 Web
应用程序中使用。而 Singleton
、Prototype``` 和 ``Application
三种作用域适用于所有环境。
(后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种)
1.3 自定义作用域
还是用上面的案例,自定义作用域要用到@Scope
注解,我们要在 Bean
存入容器的位置设置:
@Component
public class UserBeans {
//@Scope("prototye") 也可以这么写。
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//设置为 Prototype 模式
@Bean
public User user(){
User user = new User();
user.setId(1);
user.setName("小明");
return user;
}
}
结果:
现在已经成功了。
2. Bean 的生命周期
2.1 Spring 的执行流程
这里仅了解大概的流程:
启动 Spring 容器 -> 实例化 Bean -> Bean 注册到 Spring 中 -> 将 Bean 装配到需要的类中(注入)。
2.2 Bean 的生命周期
在Spring框架中,一个Bean的生命周期可以分为以下几个阶段:
- 实例化 Bean:当Spring容器加载配置文件并初始化时,它会根据Bean定义创建Bean的实例。
- 属性赋值:容器会将Bean的属性注入到相应的属性中,这些属性可能是基本类型、集合或其他Bean。
- Bean 初始化
- 各种通知:实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、 ApplicationContextAware 的接口⽅法;
- 执行初始化前置方法;
- 执行初始化方法;
- 注解方式:@PostConstruct
- xml方式:init-method 方法
- 执行初始化后置方法;
- 使用 Bean
- 销毁 Bean:如果 Bean 实现了 DisposableBean 接口,容器将调用它的 destroy() 方法;或者 Bean 可以定义一个自定义销毁方法(xml方式),使用 destroy-method 属性指定该方法;或者用 @PreDestroy 注解的方法。
前置方法和后置方法通常是指实现了 BeanPostProcessor 接口的类中的方法。具体来说,BeanPostProcessor接口定义了两个方法:postProcessBeforeInitialization() 和postProcessAfterInitialization()。前者在Bean初始化之前调用,后者在Bean初始化之后调用。
2.3 通过案例来看生命周期
我们创建如下的类。
spring-config.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="com.spring.demo.component"></content:component-scan>
<!-- 这里用了 init-method="init" 来指定初始化方法 -->
<bean id="beanComponent" class="com.spring.demo.component.BeanComponent" init-method="init"></bean>
</beans>
BeanComponent:
public class BeanComponent implements BeanNameAware {
//通知方法
@Override
public void setBeanName(String s) {
System.out.println("执行了通知 BeanName: " + s);
}
//初始化方法(用xml的方式)
public void init(){
System.out.println("用xml的方式->初始化方法");
}
//用注解的方式初始化方法
@PostConstruct
public void doPostConstruct(){
System.out.println("用注解的方式->初始化方法");
}
public void hi(){
System.out.println("执行了hi()->Hello!");
}
//销毁 用注解的方式
@PreDestroy
public void doPreDestroy(){
System.out.println("销毁:doPreDestroy");
}
}
Main:
public class Main {
public static void main(String[] args) {
//为什么是 ClassPathXmlApplicationContext? 因为它提供了destroy方法。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanComponent beanComponent = context.getBean("beanComponent",BeanComponent.class);
//调用hi方法
beanComponent.hi();
//销毁 Bean 对象
context.destroy();
}
}
结果:
(ps:如有错误,请在评论区指出,谢谢🌹)
转载自:https://juejin.cn/post/7227766568935440442