一、前言

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

二、正文

2.1 Bean作用域的种类

  在Spring官方文档中,共提到了7种不同的Bean作用域,分别是:

  • singleton(默认)
  • prototype
  • request
  • session
  • globalSession
  • application
  • websocket

  需要注意的是,前两种是Springbean的基本作用域,而后五种,算是扩展的作用域,只能在web应用中使用。下面我就来分别介绍一下这7种不同的作用域。

2.2 singleton作用域

  singleton(单例)是Spring中,bean默认的作用域。若一个bean的作用域是单例的,那么每个IoC容器只会创建这个bean的一个实例对象。所有对这个bean的依赖,以及获取这个bean的代码,拿到的都是同一个bean实例。Spring容器在创建这个bean后,会将它缓存在容器中(实际上是放在一个ConcurrentHashMap中)。Spring中的bean不是线程安全的,所以只有在我们只关注bean能够提供的功能,而不在意它的状态(属性)时,才应该使用这个作用域。下面引用一张图来看看单例bean

  需要注意的一点是,这里所说的单例,和设计模式中所提到的单例模式不同。设计模式中的单例,是强制一个类有且只有一个对象,我们如果不通过特殊的手段,将无法为这个单例类创建多个对象。而Spring中的单例作用域不同,这里的单例指的是在一个Spring容器中,只会缓存bean的唯一对象,所有通过容器获取这个bean的方式,最终拿到的都是同一个对象。但是在不同的Spring容器中,每一个Spring容器都可以拥有单例bean的一个实例对象,也就是说,这里的单例限定在一个Spring容器中,而不是整个应用程序。并且我们依然可以通过new的方式去自己创建bean

2.3 prototype作用域

  prototype可以理解为多例。若一个bean的作用域是prototype,那么Spring容器并不会缓存创建的bean,程序中对这个bean的每一次获取,容器都会重新实例化一个bean对象。通常,如果我们需要使用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文件,则通过beanscope配置项:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

2.5 session作用域

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

  与request作用域一样,session作用域只能使用在Web应用中。我们可以使用@SessionScopebean指定为session作用域,也可以使用xml配置方式:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

2.6 globalSession作用域

  这个作用域就比较特殊了,globalSession作用域的效果与session作用域类似,但是只适用于基于portletweb应用程序中。Portlet规范定义了globalSession的概念,该概念在组成单个Portlet Web应用程序的所有Portlet之间共享(引用自Spring文档)。说实话,在看到这里之前,我从来没听说过portlet。我现在所学的,基本上都是基于Servletweb应用程序,所有关于这个作用域,我也不理解。但是Spring文档中有提到一点,那就是如果我们在基于Servlet的web应用程序中使用globalSession作用域,实际上容器使用session作用域进行处理

  这个作用域也只在web应用中有效,上面也提过,具体是在基于portlet的应用中有效。文档中只提出了一种使用方式,就是基于xml

<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>

2.7 application作用域

  学过Servlet的应该对application作用域有所了解,在Servlet程序中,有一个全局的ServletContext对象,这个对象被整个web应用所共享,我们可以通过setAttribute方法向其中添加全局共享的数据。而Spring中,application作用域就是这么实现的,作用域为applicationbean,将会被作为ServletContext的属性,存储在其中,然后可以被全局访问,而且一个ServletContext只会存储这个bean的一个实例对象。ServletContext被销毁,这个bean自然也跟着被销毁。我们发现,这好像有点类似于singleton这个作用域,确实非常类似,但是也有一些区别。单例bean是一个Spring只会创建一个,而这里的却是每个ServletContext包含一个,不论有多少Spring容器,bean的数量只取决于ServletContext,而且单例bean只能通过容器去获取,是隐式的,而这种作用域的bean却是公开的,存储在ServletContext中,可直接通过ServletContext获取。

  application作用域也只能用于web应用中。使用方式和之前几种类似,可以通过@ApplicationScope注解,也可以使用xml配置文件:

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

2.8 websocket

  websocket是一种应用层的通信协议,它提供应用层的全双工通信,关于websocket协议,可以参考我的这篇博客:计算机网络——简单说说WebSocket协议。而Spring提供对websocket协议的支持,于是就有了这么一个作用域。在我看的这个Spring官方文档中,并没有对这个作用域进行详细描述,但是我们也可以通过名称推断出来。若一个bean的作用域为websocket,则只作用于一次websocket通信,若连接被释放,则bean自然也会被销毁。

2.9 单例bean依赖于多例bean

  在我们的应用程序中,可能有这样一种情况——一个作用域为singletonbean,有一个或多个作用域为prototypebean,此时将会发生什么问题。对于单例bean来说,属性注入只会发生在创建这个bean的过程中,这也就意味着,单例bean只会经历一次属性注入。也就是说,尽管这个单例bean的属性是多例的,但是由于只有一次注入,所以后续使用到的这个多例属性,永远都是同一个。此时多例就失去了意义。那该如何解决呢?

  方法主要有两种,第一种比较容易想到,就是对于单例bean的多例属性,我们不让Spring容器帮我们自动注入,而是我们自己编写一个工厂方法,在方法中通过getBean等方式,手动地向容器请求这个多例bean。由于bean是多例的,每一次getBean,实际上返回的都是一个新的实例对象。而在单例bean需要用到这个多例bean时,通过工厂方法获取。但是这种方式比较麻烦,也不利于维护。

  第二种方式就比较简单了,Spring提供了一种机制解决这个问题,那就是——方法注入。关于方法注入,可以参考我的这篇博客:Spring方法注入的使用与实现原理

三、总结

  以上就对Spring中,bean的作用域做了一个大致的介绍,至少我们知道了每一个作用域是什么,以及大致的功能,不会在被问到的时候,连是什么都不知道。以上内容是我直接参考Spring官方文档所编写,文档中的内容也不是太详细,有些描述也不是特别清晰,所以上面有些是我自己的理解,若存在不足或者错误,欢迎指正,共同进步。

四、参考

浅析Spring中bean的作用域的更多相关文章

  1. spring中bean的作用域属性singleton与prototype的区别

    1.singleton 当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会 ...

  2. 详解Spring中Bean的作用域与生命周期

    摘要:在利用Spring进行IOC配置时,关于bean的配置和使用一直都是比较重要的一部分,同时如何合理的使用和创建bean对象,也是小伙伴们在学习和使用Spring时需要注意的部分,所以这一篇文章我 ...

  3. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  4. Spring入门(五):Spring中bean的作用域

    1. Spring中bean的多种作用域 在默认情况下,Spring应用上下文中所有的bean都是以单例(singleton)的形式创建的,即不管给定的一个bean被注入到其他bean多少次,每次所注 ...

  5. java——spring中bean的作用域

    文章:理解Spring框架中Bean的作用域 博客地址:https://baijiahao.baidu.com/s?id=1610298792072480906&wfr=spider& ...

  6. JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式

    上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...

  7. Spring中bean的作用域与生命周期

    在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean.简单地讲,bean 就是由 IOC 容器初始化.装配及管理的对象,除此之外,bean 就与 ...

  8. Spring核心技术(五)——Spring中Bean的作用域

    前文概述了Spring的容器,Bean,以及依赖的一些信息,本文将描述一下Bean的作用域 Bean的作用域 当开发者定义Bean的时候,同时也会定义了该如何创建Bean实例.这些具体创建的过程是很重 ...

  9. Spring中Bean的作用域差别

    我觉得servlet和spring交叉起来,理解得快. Bean的作用域中,prototype和singleton作用域效果不一样,前者每次都会有新的实例,而后者始终一个实例 . 所以,java.ut ...

随机推荐

  1. 怎么在执行Python脚本时,密码等敏感信息也不让它出现

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http ...

  2. F - Select Half dp

    题目大意:从n个数里边选n/2个数,问和最大是多少. 题解:这是一个比较有意思的DP,定义状态dp[i][1],表示选了第i个数的最优状态,dp[i][0]表示没有选第i个数的最优状态. 状态是如何转 ...

  3. 【题解】P3959 宝藏 - 状压dp / dfs剪枝

    P3959 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的m  条道路和它们的长度. 小明决心亲自前往挖掘所有宝 ...

  4. C# LINQ查询之对象

    LINQ是一组查询技术的统称,其主要思想是将各种查询功能直接集成到C#语言中,可以对 对象.XML文档.SQL数据库.外部应用程序等进行操作. 这里面讲的简单的几个子句, 必须以from子句开头,以s ...

  5. Ansible Facts 变量详解

    Ansible Facts 变量详解与使用案例 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务数据乱放: 3. ...

  6. Jquery中 $.Ajax() 参数详解

    1.url:要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type:要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如pu ...

  7. TensorFlow keras 迁移学习

    数据的读取 import tensorflow as tf from tensorflow.python import keras from tensorflow.python.keras.prepr ...

  8. Python自学从入门到就业之函数基础(小白必看)

    函数介绍 <1>什么是函数 请看如下代码: print(" _ooOoo_ ") print(" o8888888o ") print(" ...

  9. Samba远程Shell命令注入执行漏洞

    CVE:CVE-2007-2447 原理: Samba中负责在SAM数据库更新用户口令的代码未经过滤便将用户输入传输给了/bin/sh.如果在调用smb.conf中定义的外部脚本时,通过对/bin/s ...

  10. 2019-2020-1 20199325《Linux内核原理与分析》第七周作业

    第七周作业 1.进程描述符task_struct数据结构(一) 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct数据结构很 ...