Bean作用域和生命周期
Bean作用域和生命周期
文章目录
- Bean作用域和生命周期
- 通过一个案例来看Bean作用域问题
- 案例背景
- 代码示例
- 执行结果
- 原因分析
- 作用域定义
- Bean的6种作用域
- Bean的作用域
- 单例作用域(singleton)和全局作用域(application)区别
- Bean原理分析
- Bean执行流程
- Bean 生命周期
- 初始化和设置的区别
- 操作内容
- 执行顺序
- 目的和作用
- 对Bean状态的影响
通过一个案例来看Bean作用域问题
案例背景
有一个公共 Bean 供 A 用户和 B 用户使用,A 用户修改了公共 Bean 的数据,导致 B 用户使用时出现预期外逻辑错误,预期公共 Bean 在各自类中修改不影响其他类。
代码示例
- 公共 Bean:
@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java"); return user;}
}
- A 用户使用(修改操作):
@Controller
public class BeanScopesController {@Autowiredprivate User user1;public User getUser1() {User user = user1;System.out.println("Bean 原 Name: " + user.getName());user.setName("悟空"); return user;}
}
- B 用户使用:
@Controller
public class BeanScopesController2 {@Autowiredprivate User user1;public User getUser1() {User user = user1;return user;}
}
- 测试类:
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());}
}
执行结果
A 用户修改后,B 用户读取到的 Bean 名称也变为了“悟空”
原因分析
Bean 默认是单例状态(singleton
),所有人使用的都是同一个对象,所以 A 用户的修改会影响 B 用户对 Bean 的使用
作用域定义
- 在程序中,变量的可用范围叫做作用域,或者源代码中定义变量的某个区域叫做这个变量的作用域
- 在 Spring 框架中,Bean 的作用域(Scope)和生命周期是核心概念,它们决定了 Bean 的创建、存在时间和销毁方式
Bean的6种作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring 有 6 种作用域,最后四种是基于 Spring MVC 生效的:
- singleton:单例作用域
- prototype:原型作用域(多例作用域)
- request:请求作用域
- session:回话作用域
- application:全局作用域
- websocket:HTTP WebSocket 作用域
注意后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项目中只有前两种。
在Spring框架中,Bean的作用域(Scope)和生命周期是核心概念,它们决定了Bean的创建、存在时间和销毁方式。
Bean的作用域
作用域定义了Bean实例的创建方式和生命周期范围。Spring提供了以下几种常用作用域:
-
singleton(单例,默认)
- 整个应用中只存在一个Bean实例。
- 当容器初始化时创建(可通过
lazy-init="true"
延迟到首次使用时创建)。 - 适用场景:无状态Bean(如工具类、服务层组件)。
-
prototype(原型)
- 每次从容器中获取Bean时,都会创建一个新实例。
- 容器仅负责创建,不管理销毁(需手动处理资源释放)。
- 适用场景:有状态Bean(如请求参数对象、会话相关对象)。
-
request(请求,Web环境)
- 每个HTTP请求创建一个新实例,请求结束后销毁。
- 适用场景:存储请求级别的数据(如Controller中的请求参数)。
-
session(会话,Web环境)
- 每个用户会话(Session)创建一个实例,会话结束后销毁。
- 适用场景:存储用户会话信息(如登录状态)。
-
application(应用,Web环境)
- 整个Web应用生命周期内只存在一个实例,与
singleton
类似,但作用域为ServletContext。
- 整个Web应用生命周期内只存在一个实例,与
-
websocket(WebSocket,Web环境)
- 每个WebSocket连接创建一个实例,连接关闭后销毁。
单例作用域(singleton)和全局作用域(application)区别
- singleton 是 Spring Core 的作用域;application 是 Spring Web 中的作用域
Bean原理分析
- 现在,我们来看一下Bean执行原理和生命周期总结
Bean执行流程
Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从无到有)-> Bean 注册到 Spring 中(存操作)-> 将 Bean 装配到需要的类中(取操作)
Bean 生命周期
所谓的生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期。
Bean 的生命周期分为以下 5 大部分:
- 实例化 Bean(为 Bean 分配内存空间)
- 设置属性(Bean 注入和装配)
- Bean 初始化
- 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
- 执行 BeanPostProcessor 初始化前置方法;
- 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
- 执行自己指定的 init - method 方法(如果有指定的话);
- 执行 BeanPostProcessor 初始化后置方法。
- 使用 Bean
- 销毁 Bean
销毁容器的各种方法,如 @PreDestroy、DisposableBean 接口方法、destroy - method。
执行流程如下图所示:
初始化和设置的区别
在Bean生命周期中,“设置属性” 和 “初始化” 是两个不同阶段,它们存在多方面区别:
操作内容
- 设置属性:主要涉及依赖注入,即给Bean的属性赋值。Spring 通过诸如
@Autowired
、@Value
等注解,或者 XML 配置中的<property>
标签 ,将其他Bean实例、配置值等注入到当前Bean对应的属性中 。例如,一个UserService
需要依赖UserRepository
,Spring会在设置属性阶段将UserRepository
的实例注入到UserService
中对应的userRepository
属性上 - 初始化:是在设置属性完成后,对Bean进行进一步的配置和准备工作,使其达到可用状态。这个阶段会执行一些特定的初始化方法,比如实现
InitializingBean
接口的afterPropertiesSet
方法、带有@PostConstruct
注解的方法,以及在配置文件中指定的init-method
方法 。在这些方法里,通常会进行资源的加载(如加载数据库连接池配置、读取配置文件等)、状态的检查和初始化等操作
执行顺序
在Bean生命周期中,先执行设置属性操作,完成依赖注入后,才会进入初始化阶段。只有当所有需要注入的属性都正确赋值后,才能确保初始化方法执行时,Bean处于一个相对完整且可操作的状态,避免因属性未赋值而引发的空指针等异常
目的和作用
- 设置属性:侧重于建立Bean之间的依赖关系,解决Bean运行时所需要的外部资源注入问题,让Bean在创建后能够获取到执行其功能所依赖的其他组件。
- 初始化:目的是让Bean为实际的使用做好准备,确保Bean的内部状态和外部资源都处于可用状态,从而保证Bean在被应用程序调用时能够正确、稳定地提供服务
对Bean状态的影响
- 设置属性:操作完成后,Bean的属性被赋值,但此时Bean可能还没有完全准备好执行其核心业务逻辑。例如,一个包含数据库操作的服务Bean,虽然注入了数据库操作的DAO组件,但数据库连接等相关资源可能还未初始化。
- 初始化:完成后,Bean处于就绪状态,可以被应用程序安全地调用以执行其预定功能。比如上述数据库服务Bean,在初始化阶段完成数据库连接池的初始化等操作后,就可以进行数据库的增删改查操作了。