《SpringBoot 3:入门与应用实战》第 3 章 IOC 容器中的 Bean 阅读笔记 2
3.2 Bean 的作用域
作用域是一个很关键的概念,理解这个概念对学习 Spring Framework 中 Bean 的作用域很有帮助。
3.2.1 理解作用域
Java 语言的基础概念:成员变量、方法变量、局部变量。
3.2.2 内置的作用域
Spring Framework 中内置了 6 种作用域(6.x版本)。
| 作用域类型 | 概述 |
|---|---|
| singleton | 一个 IOC 容器中只有一个 |
| proptype | 每次获取新创建一个 |
| request | 一次请求创建一个 |
| session | 一个会话创建一个 |
| application | 一个 Web 应用创建一个 |
| websocket | 一个 websocket 会话创建一个 |
3.2.3 单实例 (singleton)
Spring Framework 中默认所有的 Bean 都是单实例的,即一个 IOC 容器中只有一个。
1.创建 Bean+配置类
先创建两个简单类 Child 和 Toy。
接下来,开始编写注解配置类,为了演示 Toy 在 IOC 容器中只产生了一个 Bean,我们在配置类 BeanScopeConfiguration 中注入两个 Child 类型的 Bean,都注入 Toy 类型的 Bean。
packagecom.yangjunbo.bean.scope.examplea;importorg.springframework.stereotype.Component;@ComponentpublicclassToy{}packagecom.yangjunbo.bean.scope.examplea;publicclassChild{privateToytoy;publicvoidsetToy(Toytoy){this.toy=toy;}@OverridepublicStringtoString(){return"Child{"+"toy="+toy+'}';}}packagecom.yangjunbo.bean.scope.examplea;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;@Configuration@ComponentScan("com.yangjunbo.bean.scope.examplea")publicclassBeanScopeConfiguration{@BeanpublicChildchild1(Toytoy){Childchild=newChild();child.setToy(toy);returnchild;}@BeanpublicChildchild2(Toytoy){Childchild=newChild();child.setToy(toy);returnchild;}}2.测试运行
启动类 BeanScopeAnnoApplication,使用注解驱动 IOC 容器,并从容器中取出所有 Child 对象,打印其内部的 Toy 对象并观察。运行 main 方法,控制台中打印的两个 Child 中持有的 Toy 是同一个,证明默认情况下 IOC 容器中的 Bean 作用域的确是单实例的。
packagecom.yangjunbo.bean.scope.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassBeanScopeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanScopeConfiguration.class);ctx.getBeansOfType(Child.class).forEach((name,child)->{System.out.println(name+" : "+child);});}}3.2.4 原型 (prototype)
如果连续两次调用 getBean 方法,那么应当创建两个不同的 Bean;向两个不同的 Bean 中注入两次,也应当注入两个不同的 Bean。
1.原型 Bean 效果演示
Toy 类上标注一个额外的注解 @Scope,并声明为原型类型。
在接口 ConfigurableBeanFactory 中定义好了常量,如果担心声明作用域时拼错单词,建议直接引用对应的常量。
其他代码不需要改变,直接运行 BeanScopeAnnoApplication 的 main 方法,发现控制台打印的两个 Toy 确实不同。
2.原型 Bean 的创建时机
对于单实例 Bean 的创建时机我们已经了解,在 ApplicationContext 被初始化时就已经创建完毕,那么原型 Bean 的创建时机是什么?
原型 Bean 都是在获取时创建的。验证方法,可以给 Toy 类中添加一个无参构造方法,构造方法中打印信息到控制台。
修改启动类BeanScopeAnnoApplication,如果只做组件扫描,不加载配置类,这样就相当于只有一个 Toy 类被扫描进去了,Child 不会注册到 IOC 容器中;Toy 类的构造方法没有打印,说明容器初始化时没有创建 Toy Bean。
而如果不修改启动类,以 BeanScopeConfiguration 配置类驱动 IOC 容器,则控制台中可以看到两次 Toy 构造方法的打印。
packagecom.yangjunbo.bean.scope.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassBeanScopeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanScopeConfiguration.class);ctx.getBeansOfType(Child.class).forEach((name,child)->{System.out.println(name+" : "+child);});System.out.println("---------------------不加载配置类---------------------");ApplicationContextctx2=newAnnotationConfigApplicationContext();System.out.println("---------------------加载配置类---------------------");ApplicationContextctx3=newAnnotationConfigApplicationContext(BeanScopeConfiguration.class);}}3.2.5 Web 中的扩展作用域
- request:每次客户端向 Web 应用服务器发起一次请求,Web 服务器接收到请求后由 Spring Framework 生成一个 Bean,直到请求结束后销毁。
- session:每个客户端在与 Web 应用服务器发起会话后,Spring Framework 会为之生成一个 Bean,直到会话过期后销毁。
- application:每个 Web 应用在启动时,Spring Framework 会生成一个 Bean,直到应用停止后销毁。
- websocket:每个客户端在与 Web 应用服务器建立 WebSocket 长连接时,Spring Framework 会为之生成一个 Bean,直到断开连接后销毁。