本篇文章讨论 Spring 源码中 Bean 的 scope 作用域。
对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/blob/main/docs/ioc/10-bean-scope/
引言
在 Spring 中,Bean 的作用域(Scope)指定了实例化 Bean 的范围,Spring 提供了五种 Scope:
-
Singleton: Singleton 是 Spring 默认的作用域,它指定 Bean 的实例化为单例模式。也就是无论在程序任何地方调用 Bean,都将获得同一个实例; -
Prototype: Prototype 作用域指定每次注入或获取时都会创建新的 Bean 实例。每次请求 Bean 都会创建一个新的对象; -
Request: Request 作用域仅适用于 Web 程序,它表示在 HTTP 生命周期内的 Bean 实例。也就是在同一个 HTTP 请求中,多次请求同一个 Bean,都会返回同一个实例; -
Session: Session 作用域仅适用于 Web 程序,它表示在用户会话生命周期内的 Bean 实例。也就是在同一个会话期间多次请求同一个 Bean,每次都将获取同一个实例; -
GlobalSession: GlobalSession 作用域仅适用于 Web 程序,表示在全局会话生命周期中的 Bean 实例。如果程序是分布式环境并且支持全局会话,则该作用域可确保 Bean 在所有节点上的唯一性。
Session 和 Request 作用域
其实对于 Singleton 单例作用域和 Prototype 多例作用域我们都非常了解了,但是对于下面的 Request、Session 作用域可能很少用到。这里我们使用 SpringBoot 项目环境,测试下这两种作用域的区别:
@Scope("session")
//@Scope("request")
@RestController
public class UserController {
private String info;
@GetMapping("/get")
public Object get() {
System.out.println("get:" + info + ", class:" + this.toString());
return info;
}
@GetMapping("/set")
public void set(String val) {
info = val;
System.out.println("set:" + info + ", class:" + this.toString());
}
}
在上面使用@Scope
注解指定作用域 session 和 request;
测试步骤:
-
先请求 http://localhost:8080/set?val=1111
-
再多次请求 http://localhost:8080/get
,查看打印的 Class 信息
在@Scope("session")
配置下测试结果:
set:1111, class:cn.tycoding.spring.web.UserController@505d57bc
get:1111, class:cn.tycoding.spring.web.UserController@505d57bc
get:1111, class:cn.tycoding.spring.web.UserController@505d57bc
get:1111, class:cn.tycoding.spring.web.UserController@505d57bc
在@Scope("request")
配置下测试结果:
set:1111, class:cn.tycoding.spring.web.UserController@24c7e4f1
get:null, class:cn.tycoding.spring.web.UserController@162ccd52
get:null, class:cn.tycoding.spring.web.UserController@3ed72662
get:null, class:cn.tycoding.spring.web.UserController@40fdb86e
结论
Tips: 首先我们要知道对于同一浏览器,第一次请求时就在请求头设置了 Cookie 或者 SessionId,因此同一个浏览器的不同 Tab 窗口都认为是在同一个会话中;不同浏览器或者无痕模式则被认为是不同的会话。
从上面结果中可以看出:
-
在 @Scope("session")
模式下,多次请求/get
接口都被认为是在同一个会话中,数据在同一个 Session 下是共享的,请求的也是同一个 bean 实例对象(Class 相同) -
在 @Scope("request")
模式下,多次请求/get
接口每次都返回新的 Class 实例,数据即使在同一个会话下也不是共享的
如何保证在一个会话中?我们知道 SpringBoot 其实是默认使用内置的 Tomcat 容器提供 Web 服务,因此对于 Web 容器在接受到浏览器请求时,会自动生成一个 Cookie(或者说 SessionId)字段并设置在 Request Header 中,因此之后同域名下的每次请求都认为是相同会话并使用同一个 Cookie(活 SessionId)
Singleton 和 Prototype 作用域
我们都知道 Singleton 是单例模式,Spring 默认使用 Singleton 单例作用域,也就是在默认的 Singleton 模式下 Spring 使用了三级缓存缓存单例 Bean 数据(为什么用三级主要是解决循环依赖);
而 Prototype 作用域通常就是我们说的多例模式,多例模式下 Spring 并不会缓存 Bean 实例,而是每次调用都创建一个新的 Bean 实例;
在 Spring 源码中,可以定位AbstractBeanFactory
类的doGetBean
函数,我们知道这个函数用于获取一个 Bean 实例:
如上其实 Spring 在创建 Bean 实例时候进行了 Scope 判断(Scope 信息来自 BeanDefinition 对象),对于 Singleton 作用域是从getSingleton()
函数中获取单例 Bean 实例;而对于 Prototype 作用域则是直接调用createBean
创建新的 Bean 实例对象。
到此为止,我们再看一下 Bean 生命周期
Spring 源码专栏
此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题。
配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs
联系我
-
个人博客:http://tycoding.cn/ -
GitHub:https://github.com/tycoding -
微信公众号:程序员涂陌 -
QQ 交流群:866685601
原文始发于微信公众号(程序员涂陌):Spring 源码分析——Bean 作用域
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145564.html