Spring 源码分析——Bean 作用域


本篇文章讨论 Spring 源码中 Bean 的 scope 作用域。

对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/blob/main/docs/ioc/10-bean-scope/

引言

在 Spring 中,Bean 的作用域(Scope)指定了实例化 Bean 的范围,Spring 提供了五种 Scope:

  1. Singleton: Singleton 是 Spring 默认的作用域,它指定 Bean 的实例化为单例模式。也就是无论在程序任何地方调用 Bean,都将获得同一个实例;
  2. Prototype: Prototype 作用域指定每次注入或获取时都会创建新的 Bean 实例。每次请求 Bean 都会创建一个新的对象;
  3. Request: Request 作用域仅适用于 Web 程序,它表示在 HTTP 生命周期内的 Bean 实例。也就是在同一个 HTTP 请求中,多次请求同一个 Bean,都会返回同一个实例;
  4. Session: Session 作用域仅适用于 Web 程序,它表示在用户会话生命周期内的 Bean 实例。也就是在同一个会话期间多次请求同一个 Bean,每次都将获取同一个实例;
  5. 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;

测试步骤:

  1. 先请求http://localhost:8080/set?val=1111
  2. 再多次请求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 窗口都认为是在同一个会话中;不同浏览器或者无痕模式则被认为是不同的会话。

从上面结果中可以看出:

  1. @Scope("session")模式下,多次请求/get接口都被认为是在同一个会话中,数据在同一个 Session 下是共享的,请求的也是同一个 bean 实例对象(Class 相同
  2. @Scope("request")模式下,多次请求/get接口每次都返回新的 Class 实例,数据即使在同一个会话下也不是共享的

如何保证在一个会话中?我们知道 SpringBoot 其实是默认使用内置的 Tomcat 容器提供 Web 服务,因此对于 Web 容器在接受到浏览器请求时,会自动生成一个 Cookie(或者说 SessionId)字段并设置在 Request Header 中,因此之后同域名下的每次请求都认为是相同会话并使用同一个 Cookie(活 SessionId)

Spring 源码分析——Bean 作用域
image-20230522095337090

Singleton 和 Prototype 作用域

我们都知道 Singleton 是单例模式,Spring 默认使用 Singleton 单例作用域,也就是在默认的 Singleton 模式下 Spring 使用了三级缓存缓存单例 Bean 数据(为什么用三级主要是解决循环依赖);

而 Prototype 作用域通常就是我们说的多例模式,多例模式下 Spring 并不会缓存 Bean 实例,而是每次调用都创建一个新的 Bean 实例;

在 Spring 源码中,可以定位AbstractBeanFactory类的doGetBean函数,我们知道这个函数用于获取一个 Bean 实例:

Spring 源码分析——Bean 作用域
image-20230522104314181

如上其实 Spring 在创建 Bean 实例时候进行了 Scope 判断(Scope 信息来自 BeanDefinition 对象),对于 Singleton 作用域是从getSingleton()函数中获取单例 Bean 实例;而对于 Prototype 作用域则是直接调用createBean创建新的 Bean 实例对象。


到此为止,我们再看一下 Bean 生命周期

Spring 源码分析——Bean 作用域
img

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

(1)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!