Spring Framework支持五种作用域(其中有三种只能用在基于web的Spring ApplicationContext)。

singleton

在每个Spring IoC容器中一个bean定义对应一个对象实例。

prototype

一个bean定义对应多个对象实例。

request

在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。

session

在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

global session

在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

[@more@]

1.Singleton作用域

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

换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。

下图演示了Spring的singleton作用域。

请注意Spring的singleton bean概念与“四人帮”(GoF)模式一书中定义的Singleton模式是完全不同的。经典的GoF Singleton模式中所谓的对象范围是指在每一个ClassLoader中指定class创建的实例有且仅有一个。把Spring的singleton作用域描述成一个container对应一个bean实例最为贴切。亦即,假如在单个Spring容器内定义了某个指定class的bean,那么Spring容器将会创建一个且仅有一个由该bean定义指定的类实例。

Singleton作用域是Spring中的缺省作用域。

2.Prototype

Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

下图演示了Spring的prototype作用域。请注意,典型情况下,DAO不会被配置成prototype,因为一个典型的DAO不会持有任何会话状态,因此应该使用singleton作用域。

对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

谈及prototype作用域的bean时,在某些方面你可以将Spring容器的角色看作是Java new操作符的替代者。任何迟于该时间点的生命周期事宜都得交由客户端来处理。在Section 3.5.1, “Lifecycle接口”一节中会进一步讲述Spring IoC容器中的bean生命周期。

 

向后兼容性:在XML中指定生命周期作用域

如果你在bean定义文件中引用'spring-beans.dtd' DTD,要显式说明bean的生命周期作用域你必须使用"singleton"属性(记住singleton生命周期作用域是默认的)。 如果引用的是'spring-beans-2.0.dtd' DTD或者是Spring 2.0 XSD schema,那么需要使用"scope"属性(因为"singleton"属性被删除了,新的DTD和XSD文件使用"scope"属性)。

简单地说,如果你用"singleton"属性那么就必须在那个文件里引用'spring-beans.dtd' DTD。 如果你用"scope"属性那么必须 在那个文件里引用'spring-beans-2.0.dtd' DTD 或'spring-beans-2.0.xsd' XSD。

3. 其他作用域

其他作用域,即request、session以及global session仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架)。

 

Note

下面介绍的作用域仅仅在使用基于web的Spring ApplicationContext实现(如XmlWebApplicationContext)时有用。如果在普通的Spring IoC容器中,比如像XmlBeanFactory或ClassPathXmlApplicationContext,尝试使用这些作用域,你将会得到一个IllegalStateException异常(未知的bean作用域)。

3.1. 初始化web配置

要使用request、session和 global session作用域的bean(即具有web作用域的bean),在开始设置bean定义之前,还要做少量的初始配置。请注意,假如你只想要“常规的”作用域,也就是singleton和prototype,就不需要这一额外的设置。

在目前的情况下,根据你的特定servlet环境,有多种方法来完成这一初始设置。如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可

org.springframework.web.context.request.RequestContextListener

如果你用的是早期版本的web容器(Servlet 2.4以前),那么你要使用一个javax.servlet.Filter的实现。请看下面的web.xml配置片段:

requestContextFilter

org.springframework.web.filter.RequestContextFilter

requestContextFilter

/*

3.2. Request作用域

考虑下面bean定义:

针对每次HTTP请求,Spring容器会根据loginAction bean定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

3.3. Session作用域

考虑下面bean定义:

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

3.4. global session作用域

考虑下面bean定义:

global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。

请注意,假如你在编写一个标准的基于Servlet的web应用,并且定义了一个或多个具有global session作用域的bean,系统会使用标准的HTTP Session作用域,并且不会引起任何错误。

3.5. 作用域bean与依赖

能够在HTTP request或者Session(甚至自定义)作用域中定义bean固然很好,但是Spring IoC容器除了管理对象(bean)的实例化,同时还负责协作者(或者叫依赖)的实例化。如果你打算将一个Http request范围的bean注入到另一个bean中,那么需要注入一个AOP代理来替代被注入的作用域bean。也就是说,你需要注入一个代理对象,该对象具有与被代理对象一样的公共接口,而容器则可以足够智能的从相关作用域中(比如一个HTTP request)获取到真实的目标对象,并把方法调用委派给实际的对象。

 

不能和作用域为singleton或prototype的bean一起使用。为singleton bean创建一个scoped proxy将抛出BeanCreationException异常。

让我们看一下将相关作用域bean作为依赖的配置,配置并不复杂(只有一行),但是理解“为何这么做”以及“如何做”是很重要的。

在XML配置文件中,要创建一个作用域bean的代理,只需要在作用域bean定义里插入一个子元素即可(你可能还需要在classpath里包含CGLIB库,这样容器就能够实现基于class的代理;还可能要使用基于XSD的配置)。上述XML配置展示了“如何做”,现在讨论“为何这么做”。在作用域为request、session以及globalSession的bean定义里,为什么需要这个元素呢?下面我们从去掉元素的XML配置开始说起:

从上述配置中可以很明显的看到singleton bean userManager被注入了一个指向HTTP Session作用域bean userPreferences的引用。singleton userManager bean会被容器仅实例化一次,并且其依赖(userPreferences bean)也仅被注入一次。这意味着,userManager在理论上只会操作同一个userPreferences对象,即原先被注入的那个bean。而注入一个HTTP Session作用域的bean作为依赖,有违我们的初衷。因为我们想要的只是一个userManager对象,在它进入一个HTTP Session生命周期时,我们希望去使用一个HTTP Session的userPreferences对象。

当注入某种类型对象时,该对象实现了和UserPreferences类一样的公共接口(即UserPreferences实例)。并且不论我们底层选择了何种作用域机制(HTTP request、Session等等),容器都会足够智能的获取到真正的UserPreferences对象,因此我们需要将该对象的代理注入到userManager bean中, 而userManager bean并不会意识到它所持有的是一个指向UserPreferences引用的代理。在本例中,当UserManager实例调用了一个使用UserPreferences对象的方法时,实际调用的是代理对象的方法。随后代理对象会从HTTP Session获取真正的UserPreferences对象,并将方法调用委派给获取到的实际的UserPreferences对象。

这就是为什么当你将request、session以及globalSession作用域bean注入到协作对象中时需要如下正确而完整的配置:Java代码

4自定义作用域

在Spring 2.0中,Spring的bean作用域机制是可以扩展的。这意味着,你不仅可以使用Spring提供的预定义bean作用域; 还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)。

作用域由接口org.springframework.beans.factory.config.Scope定义。要将你自己的自定义作用域集成到Spring容器中,需要实现该接口。它本身非常简单,只有两个方法,分别用于底层存储机制获取和删除对象。自定义作用域可能超出了本参考手册的讨论范围,但你可以参考一下Spring提供的Scope实现,以便于去如何着手编写自己的Scope实现。

在实现一个或多个自定义Scope并测试通过之后,接下来就是如何让Spring容器识别你的新作用域。ConfigurableBeanFactory接口声明了给Spring容器注册新Scope的主要方法。(大部分随Spring一起发布的BeanFactory具体实现类都实现了该接口);该接口的主要方法如下所示:

void registerScope(String scopeName, Scope scope);

registerScope(..)方法的第一个参数是与作用域相关的全局唯一名称;Spring容器中该名称的范例有singleton和prototype。registerScope(..)方法的第二个参数是你打算注册和使用的自定义Scope实现的一个实例。

假设你已经写好了自己的自定义Scope实现,并且已经将其进行了注册:

// note: the ThreadScope class does not exist; I made it up for the sake of this example

Scope customScope = new ThreadScope();

beanFactory.registerScope("thread", scope);

然后你就可以像下面这样创建与自定义Scope的作用域规则相吻合的bean定义了:

如果你有自己的自定义Scope实现,你不仅可以采用编程的方式注册自定义作用域,还可以使用BeanFactoryPostProcessor实现:CustomScopeConfigurer类,以声明的方式注册Scope。BeanFactoryPostProcessor接口是扩展Spring IoC容器的基本方法之一,在本章的BeanFactoryPostProcessor中将会介绍。

使用CustomScopeConfigurer,以声明方式注册自定义Scope的方法如下所示:

Java代码既允许你指定实际的Class实例作为entry的值,也可以指定实际的Scope实现类实例;详情请参见CustomScopeConfigurer类的JavaDoc。

Spring-bean作用域scope详解的更多相关文章

  1. spring中的scope详解

    spring容器中的bean默认是单例模式的,改成非单例模式需要在类上加上@Scope("prototype") 1. scope概论 spring中scope是一个非常关键的概念 ...

  2. 【Spring】IoC容器 - Spring Bean作用域Scope(含SpringCloud中的RefreshScope )

    前言 上一章学习了[依赖来源],本章主要讨论SpringBean的作用域,我们这里讨论的Bean的作用域,很大程度都是默认只讨论依赖来源为[Spring BeanDefinition]的作用域,因为在 ...

  3. Spring中bean的作用域scope详解

    参考文献:http://blog.csdn.net/jacklearntech/article/details/40157861 http://www.cnblogs.com/qq78292959/p ...

  4. Spring中bean的scope详解

    如何使用spring的作用域: <bean id="role" class="spring.chapter2.maryGame.Role" scope=& ...

  5. Spring Bean生命周期详解

    对象生命周期:创建(实例化----初始化)---使用----销毁,而在Spring中,Bean对象周期当然遵从这一过程,但是Spring提供了许多对外接口,允许开发者对三个过程(实例化.初始化.销毁) ...

  6. spring bean parent属性详解

    必要条件:1.子bean必须与父bean保持兼容,也就是说子bean中必须有父bean定义的所有属性. 2.父bean必须是抽象bean或者定义lazy-init=true也就是不让bean工厂实例化 ...

  7. Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)

    上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...

  8. Spring IoC createBean 方法详解

    前言 本篇文章主要分析 Spring IoC 的 createBean() 方法的流程,以及 bean 的生命周期. 下面是一个大致的流程图: 正文 AbstractAutowireCapableBe ...

  9. Spring Data操作Redis详解

    Spring Data操作Redis详解 Redis是一种NOSQL数据库,Key-Value形式对数据进行存储,其中数据可以以内存形式存在,也可以持久化到文件系统.Spring data对Redis ...

随机推荐

  1. 写出稳定的Modbus代码之点滴经验

    1.引言 Modbus是工业领域重要的协议,物理层有常见的RS485双绞线和TCP,所以又常说Modbus 485开发和Modbus TCP开发. 前者就是串口通信,比较简单.后者涉及到网络协议,复杂 ...

  2. JavaScript Array 技巧

    filter():返回该函数会返回true的项组成的数组 ,,,,]; var result = num.filter(function(item,index,array){ ); }) consol ...

  3. Parse error: syntax error, unexpected '[' in D:\phpStudy\WWW\tp5\thinkphp\library\think\Loader.php on line 18

    g刚学习tp5就遇到了这个问题  百思不得其解,看到官网说明 是基于PHP5.4 设计的  打开 phpstudy版本一看 就呵呵呵了 .还是5.3的版本.更换版本之后 就ok了.

  4. Doctype 文档类型,标准模式,混杂模式

    HTML4.01和XHTML1.0 基于 SGML,支持DTD声明,HTML5不是,但是需要 doctype 来规范浏览器的行为. 标准模式是指,DTD声明定义了标准文档的类型后,浏览器按W3C标准解 ...

  5. 手把手视频:万能开源Hawk抓取动态网站

    Hawk是沙漠之鹰历时五年开发的开源免费网页抓取工具(爬虫),无需编程,全部可视化. 自从上次发布Hawk 2.0过了小半年,可是还是有不少朋友通过邮件或者微信的方式询问如何使用.看文档还是不如视频教 ...

  6. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  7. JS中substring与substr的区别

    Substring: 该方法可以有一个参数也可以有两个参数. (1)  一个参数: 示例: var str=“Olive”: str.substring(3); 结果:“ve” 说明:当substri ...

  8. 【从无到有】HTML的初识——part1

    Ⅰ.HTML的初识 1.HTML:超文本标签语言(网页源代码) 2.html的基本结构: <html> <head> <meta charset="utf-8& ...

  9. ActiveMQ学习心得:连接池的简单实现和模板模式的应用

    一.安装activemq 下载地址:https://archive.apache.org/dist/activemq/5.13.0/apache-activemq-5.13.0-bin.zip 下载完 ...

  10. PMBOK 学习与实践分享视频

    本系列为自己在学习PMBOK时进行的总结与分享,每一节主要包括两部分: 对PMBOK本身的一个结构笔记和讲解. 对自己项目管理工作的一个总结和思考. PMBOK 学习与实践分享视频内容清单 人力资源管 ...