浅析Spring中bean的作用域 bean有什么用

一、前言
刚刚花了点时间,阅读了一下 Spring 官方文档中,关于 bean 的作用域这一块的内容 。Spring-4.3.21 官方文档中,共介绍了七种 bean 作用域,这篇博客就来简单介绍一下这七种作用域的含义 。毕竟只是阅读了一下文档,没有实际的使用经验,所有对于这些作用域的理解比较浅显,这篇博客就当是记笔记,或者也可以算是翻译文档中的内容了 。
二、正文2.1 Bean作用域的种类在 Spring 官方文档中,共提到了 7 种不同的 Bean 作用域,分别是:

  • singleton(默认)
  • prototype
  • request
  • session
  • globalSession
  • application
  • websocket
需要注意的是,前两种是 Spring 中 bean 的基本作用域,而后五种,算是扩展的作用域,只能在 web 应用中使用 。下面我就来分别介绍一下这 7 种不同的作用域 。
2.2 singleton作用域singleton(单例)是Spring中,bean默认的作用域 。若一个 bean 的作用域是单例的,那么每个 IoC 容器只会创建这个 bean 的一个实例对象 。所有对这个 bean 的依赖,以及获取这个 bean 的代码,拿到的都是同一个 bean 实例 。Spring 容器在创建这个 bean 后,会将它缓存在容器中(实际上是放在一个 ConcurrentHashMap 中) 。Spring中的bean不是线程安全的,所以只有在我们只关注bean能够提供的功能,而不在意它的状态(属性)时,才应该使用这个作用域。下面引用一张图来看看单例 bean :
浅析Spring中bean的作用域 bean有什么用

文章插图
需要注意的一点是,这里所说的单例,和设计模式中所提到的单例模式不同 。设计模式中的单例,是强制一个类有且只有一个对象,我们如果不通过特殊的手段,将无法为这个单例类创建多个对象 。而 Spring 中的单例作用域不同,这里的单例指的是在一个 Spring 容器中,只会缓存 bean 的唯一对象,所有通过容器获取这个 bean 的方式,最终拿到的都是同一个对象 。但是在不同的 Spring 容器中,每一个 Spring 容器都可以拥有单例 bean 的一个实例对象,也就是说,这里的单例限定在一个 Spring 容器中,而不是整个应用程序 。并且我们依然可以通过 new 的方式去自己创建 bean。
2.3 prototype作用域prototype 可以理解为多例 。若一个 bean 的作用域是 prototype ,那么 Spring 容器并不会缓存创建的 bean ,程序中对这个 bean 的每一次获取,容器都会重新实例化一个 bean 对象 。通常,如果我们需要使用bean的状态(属性),且这个状态是会改变的,那么我们就可以将它配置为这个作用域,以解决线程安全的问题。因为对于单例 bean 来说,多个线程共享它的可变属性,会存在线程安全问题 。下面引用一张图来描述这个作用域:
浅析Spring中bean的作用域 bean有什么用

文章插图
前面也提过,如果 bean 的作用域是 prototype 的,那么容器在创建完这个 bean 后,并不会将它保存在容器中,这也就意味着,Spring 容器并不能为我们做这个对象的销毁工作(比如资源释放) 。此时我们可以通过 Spring 提供的接口,自定义一个后处理器,然后将这些 bean 的引用存储在这个后处理器中,当容器回调这个后处理器的方法时,我们可以在方法中通过提前存储的 bean 的引用,将它们销毁 。
2.4 request作用域request 作用域将 bean 的使用范围限定在一个 http 请求中,对于每一个请求,都会单独创建一个 bean ,若请求结束,bean 也会随之销毁 。使用 request 作用域一般不会存在线程安全问题,因为在 Web 应用中,每个请求都是由一个单独的线程进行处理,所有线程之间并不会共享 bean ,从而不会存在线程安全的问题 。
这个作用域只能使用在 Web 应用中 。如果使用的是注解扫描配置 bean ,那么在 bean 所属的类上使用 @RequestScope 注解即可使用此作用域,若是基于 xml 文件,则通过 bean 的 scope 配置项:

2.5 session作用域session 作用域将 bean 的使用范围一次在一次 http会话 中,对于每一个会话,Spring 容器都会创建一个单独的 bean ,若 session 被销毁,则 bean 也随之销毁 。我们可以修改 bean 的状态,这个修改只对当前会话可见,但是是否线程安全呢? Spring 文档中并未提及,但我认为不是线程安全的,因为每一个 session 可以对应于多个 request ,这些请求不一定就是串行执行的,比如说用户打开多个界面,同时进行多次操作,那后台将同时处理同一个 session 的多个 request ,此时并不能保证 bean 的线程安全 。