| 作用域 | 描述 |
|---|---|
singleton | 在每个Spring IoC容器中一个bean定义对应一个对象实例。 |
prototype | 一个bean定义对应多个对象实例。 |
request | 在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring |
在一个HTTP | |
global session | 在一个全局的HTTP |
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针
对该bean的后续请求和引用都将返回被缓存的对象实例。
Prototype作用域Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对所
有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
其他作用域
request、session以及global session仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架)。
注意
下面介绍的作用域仅仅在使用基于web的SpringApplicationContext实现(如xmlWebApplicationContext)时有用。如果在普通的Spring IoC容器中,比如像XmlBeanFactory
或ClassPathXmlApplicationContext,尝试使用这些作用域,你将会得到一个IllegalStateException异常(未知的bean作用域)。
初始化web配置
要使用request、session和global session作用域的bean(即具有web作用域的bean),在开始设置bean定义之前,还要做少量的初始配置。请注意,假如你只想要“常规的”作用域,也就是singleton和prototype,就不需要这一额外的设置。
在目前的情况下,根据你的特定servlet环境,有多种方法来完成这一初始设置。如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
如果你用的是早期版本的web容器(Servlet 2.4以前),那么你要使用一个javax.servlet.Filter的实现。请看下面的web.xml配置片段:
<filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
RequestContextListener和RequestContextFilter两个类做的都是同样的工作:将HTTP request对象绑定到为该请求提供服务的Thread。这使得具有request和session作用域的bean能够在后面的调用链中被访问到。
针对每次HTTP请求,Spring容器会根据loginActionbean定义创建一个全新的LoginActionbean实例,且该loginActionbean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginActionbean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTPSession,Spring容器会根据userPreferencesbean定义创建一个全新的userPreferencesbean实例,且该userPreferencesbean仅在当前HTTPSession内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTPSession中根据userPreferences创建的实例,将不会看到这些特定于某个HTTPSession的状态变化。当HTTPSession最终被废弃的时候,在该HTTPSession作用域内的bean也会被废弃掉。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
global session作用域类似于标准的HTTPSession作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portletSession的生命周期范围内。
请注意,假如你在编写一个标准的基于Servlet的web应用,并且定义了一个或多个具有global session作用域的bean,系统会使用标准的HTTPSession作用域,并且不会引起任何错误。
能够在HTTP request或者Session(甚至自定义)作用域中定义bean固然很好,但是Spring IoC容器除了管理对象(bean)的实例化,同时还负责协作者(或者叫依赖)的实例化。如果你打算将一个Http request范围的bean注入到另一个bean中,那么需要注入一个AOP代理来替代被注入的作用域bean。也就是说,你需要注入一个代理对象,该对象具有与被代理对象一样的公共接口,而容器则可以足够智能的从相关作用域中(比如一个HTTP request)获取到真实的目标对象,并把方法调用委派给实际的对象。
<aop:scoped-proxy/>不能和作用域为singleton或prototype的bean一起使用。为singleton bean创建一个scoped proxy将抛出BeanCreationException异常。
让我们看一下将相关作用域bean作为依赖的配置,配置并不复杂(只有一行),但是理解“为何这么做”以及“如何做”是很重要的。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <aop:scoped-proxy/> </bean> <bean id="userService" class="com.foo.SimpleUserService"> <property name="userPreferences" ref="userPreferences"/> </bean>
在XML配置文件中,要创建一个作用域bean的代理,只需要在作用域bean定义里插入一个<aop:scoped-proxy/>子元素即可(你可能还需要在classpath里包含CGLIB库,这样容器就能够实现基于class的代理;还可能要使用基于XSD的配置)。上述XML配置展示了“如何做”,现在讨论“为何这么做”。在作用域为request、session以及globalSession的bean定义里,为什么需要这个<aop:scoped-proxy/>元素呢?下面我们从去掉<aop:scoped-proxy/>元素的XML配置开始说起:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>
从上述配置中可以很明显的看到singleton beanuserManager被注入了一个指向HTTPSession作用域beanuserPreferences的引用。singletonuserManagerbean会被容器仅实例化一次,并且其依赖(userPreferencesbean)也仅被注入一次。这意味着,userManager在理论上只会操作同一个userPreferences对象,即原先被注入的那个bean。而注入一个HTTPSession作用域的bean作为依赖,有违我们的初衷。因为我们想要的只是一个userManager对象,在它进入一个HTTPSession生命周期时,我们希望去使用一个HTTPSession的userPreferences对象。
当注入某种类型对象时,该对象实现了和UserPreferences类一样的公共接口(即UserPreferences实例)。并且不论我们底层选择了何种作用域机制(HTTP request、Session等等),容器都会足够智能的获取到真正的UserPreferences对象,因此我们需要将该对象的代理注入到userManagerbean中, 而userManagerbean并不会意识到它所持有的是一个指向UserPreferences引用的代理。在本例中,当UserManager实例调用了一个使用UserPreferences对象的方法时,实际调用的是代理对象的方法。随后代理对象会从HTTPSession获取真正的UserPreferences对象,并将方法调用委派给获取到的实际的UserPreferences对象。
自定义作用域
详情百度,知道有这么个东西就行了
新闻热点
疑难解答