当通过spring容器创建一个bean实例时,不仅可以完成bean实例的实例化,还可以为bean指定特定的作用域。spring支持如下5种作用域:
singleton:单例模式,在整个spring ioc容器中,使用singleton定义的bean将只有一个实例
prototype:原型模式,每次通过容器的getbean方法获取prototype定义的bean时,都将产生一个新的bean实例
request:对于每次http请求,使用request定义的bean都将产生一个新实例,即每次http请求将会产生不同的bean实例。只有在web应用中使用spring时,该作用域才有效
session:对于每次http session,使用session定义的bean豆浆产生一个新实例。同样只有在web应用中使用spring时,该作用域才有效
globalsession:每个全局的http session,使用session定义的bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在web应用中使用spring时,该作用域才有效
其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的bean,每次请求该bean都将获得相同的实例。容器负责跟踪bean实例的状态,负责维护bean实例的生命周期行为;如果一个bean被设置成prototype作用域,程序每次请求该id的bean,spring都会新建一个bean实例,然后返回给程序。在这种情况下,spring容器仅仅使用new 关键字创建bean实例,一旦创建成功,容器不在跟踪实例,也不会维护bean实例的状态。
如果不指定bean的作用域,spring默认使用singleton作用域。java在创建java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域bean的创建、销毁代价比较大。而singleton作用域的bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将bean被设置成prototype作用域。
设置bean的基本行为,通过scope属性指定,该属性可以接受singleton、prototype、request、session、globlesession5个值,分别代表以上5种作用域。下面的配置片段中,singleton和prototype各有一个:
<a href="http://my.oschina.net/itblog/blog/203436#">?</a>
1
2
3
4
<code><!-- 默认的作用域:singleton --></code>
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"p1"</code> <code>class</code><code>=</code><code>"com.abc.person"</code> <code>/> </code>
<code><!-- 指定的作用域:prototype --></code>
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"p2"</code> <code>class</code><code>=</code><code>"com.abc.person"</code> <code>scope</code><code>=</code><code>"prototype"</code> <code>/></code>
下面是一个测试类:
5
6
7
8
9
<code>public</code> <code>class</code> <code>beantest {</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(string args[]) {</code>
<code> </code><code>//加载类路径下的beans.xml文件以初始化spring容器</code>
<code> applicationcontext context = </code><code>new</code> <code>classpathxmlapplicationcontext();</code>
<code> </code><code>//分两次分别取同一个bean,比较二者是否是同一个对象</code>
<code> system.out.println(context.getbean(</code><code>"p1"</code><code>) == context.getbean(</code><code>"p1"</code><code>));</code>
<code> system.out.println(context.getbean(</code><code>"p2"</code><code>) == context.getbean(</code><code>"p2"</code><code>));</code>
<code> }</code>
<code>}</code>
执行结果分别是:true和false
从结果可以看出,正如上文所述:对于singleton作用域的bean,每次请求该id的bean,都将返回同一个实例,而prototype作用域的bean, 每次请求都将产生全新的实例。
注意:早期指定bean的作用域也可通过singleton属性指定,该属性只接受两个属性值:true和false,分别代表singleton和prototype的作用域。使用singleton属性则无法指定其他三个作用域。实际上spring2.x不推荐使用singleton属性指定bean的作用域,singleton属性是spring 1.2.x的使用方式。
对于request作用域,查看如下bean定义:
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"loginaction"</code> <code>class</code><code>=</code><code>"com.abc.loginaction"</code> <code>scope</code><code>=</code><code>"request"</code> <code>/></code>
针对每次http请求,spring容器会根据loginactionbean定义创建一个全新的loginaction实例,且该loginaction实例尽在当前http request内有效。因此,如果程序需要,完全可以自由更改bean实例的内部状态;其他请求所获得的loginaction实例无法感觉到这种内部状态的改变。当处理请求结束时,request作用域的bean将会被销毁。
注意:request、session作用域的bean只对web应用才真正有效。实际上通常只会将web应用的控制器bean才指定成request作用域
session作用域与request作用域完全类似,区别在于:request作用域的bean对于每次http请求有效,而session作用域的bean对于每次session有效。在web应用中,为了让request和session作用域生效,必须将http请求对象绑定到为该请求提供服务的线程上,这使得具有request和session作用域的bean实例能够在后面的调用链中被访问到。
为此我们有两种配置方式:采用listener配置或者采用filter配置。当使用servlet 2.4及以上规范的web容器时,我们可以在web应用的web.xml文件中增加listener配置,该listener负责为request作用域生效:
<code><</code><code>listener</code><code>></code>
<code> <</code><code>listener-class</code><code>></code>
<code> org.springframework.web.context.request.requestcontextlistener</code>
<code> </</code><code>listener-class</code><code>></code>
<code></</code><code>listener</code><code>></code>
如果使用了只支持servlet 2.4以前规范的web容器,则该容器不支持listener规范,故无法使用这种配置方式,只能改为使用filter配置方式,配置片段如下:
10
<code><</code><code>filter</code><code>></code>
<code> <</code><code>filter-name</code><code>>requestcontextfilter</</code><code>filter-name</code><code>></code>
<code> <</code><code>filter-class</code><code>></code>
<code> org.springframework.web.filter.requestcontextfilter</code>
<code> </</code><code>filter-class</code><code>></code>
<code></</code><code>filter</code><code>></code>
<code><</code><code>filter-mapping</code><code>></code>
<code> <</code><code>filter-name</code><code>>requestcontextfilter</</code><code>filter-name</code><code>></code>
<code> <</code><code>url-pattern</code><code>>/*</</code><code>url-pattern</code><code>></code>
<code></</code><code>filter-mapping</code><code>></code>
一旦在web.xml中增加了如上任意一种配置,程序就可以在spring配置文件中使用request或者session作用域了。下面是spring配置文件的片段:
<code><</code><code>bean</code> <code>id</code><code>=</code><code>"p3"</code> <code>class</code><code>=</code><code>"com.abc.person"</code> <code>scope</code><code>=</code><code>"request"</code> <code>/></code>
这样,spring容器会每次http请求都生成一个person实例,当该请求响应结束时,该实例也随之消失。
如果web应用直接使用spring mvc作为mvc框架,即使用springdispatcherservlet或dispatcherportlet来连接所有用户请求,则无需这些额外的配置,因为他们已经处理了所有和请求有关的状态处理。
注意:spring 3.0 不仅可以为bean指定已经存在的5个作用域,还支持自定义作用域,关于自定义作用域的内容,请参看spring官方文档等资料。