标签:
当你配置一个bean的时候,你会通过bean的配置为实际的类实例创建一个配方(recipe )。bean的配置是一个配方的想法是很重要,因为它意味着,就像类一样,你可以通过一个配方来创建很多个实例。
你不仅可以控制被注入到由特定bean配置创建的对象的依赖和配置值,而且也可以控制对象的范围。这个方式是强大和灵活的,通过这种方式你可以通过配置文件来选择对象的范围,而不用放到Java类的级别。可以用多个范围内的一个来定义bean加载的范围:开箱即用,Spring支持5类范围,其中3个是只有你用到web-aware的ApplicationContext时才会有用。
下列的范围都是可以直接使用的。你也可以创建一个自定义范围(a custom scope)。
Table 6.3. Bean scopes
范围 | 描述 |
---|---|
默认,一个bean定义在每个Spring容器中只会产生一个对象实例。 |
|
一个bean定义会产生任意数量的对象实例 |
|
一个bean定义的范围是在HTTP请求的生命周期内;这也就是,每一个HTTP请求都有自己的一个bean定义创建的bean定义。只有在基于web的SpringApplicationContext的上下文中才有效。 |
|
一个bean定义的范围是在HTTP Session的生命周期内,只有在基于web的SpringApplicationContext的上下文中才有效。 | |
一个bean的定义的范围是在全局HTTPSession生命周期内。通常在Portlet容器中是不可用的。只有在基于web的SpringApplicationContext的上下文中才有效。 |
|
一个bean的范围是在ServletContext的生命周期内。只有在基于web的SpringApplicationContext的上下文中才有效。 |
在spring 3.0中,线程范围的bean也是可用的,但不是默认注册的。想了解更多信息,请参考 |
只维护一个共享的bean实例,任何通过id或ids匹配的bean的请求,Spring都会返一个特定的bean的实例。
换种方式说,当你定义一个bean的范围为singleton时,Spring容器会根据bean的配置恰好创建一个对象实例。这个单例bean被存在这些单例bean的缓存中,任何后续对于同一个bean的请求或者引用都会返回缓存对象。
Spring概念的单例bean和设计模式中的单例是不同的。设计模式的单例是通过硬编码的方式使一个对象在每一个ClassLoder中只会创建仅仅一个特定类的实例。Spring Bean的单例范围是对一个容器一个bean的最好描述。这意味着如果你在一个Spring容器中只为一个类定义了一个bean,那么Spring只会根据配置文件创建一个指定类的实例。singleton 范围是Spring默认的范围。用XML定义单例bean,你应该如例子中的写法:
<bean id="accountService" class="com.foo.DefaultAccountService"/> <!-- the following is equivalent, though redundant (singleton scope is the default) --> <bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>和其他范围不同,Spring不会管理prototype的完整的生命周期:容器初始化,配置,和另外的装配prototype 对象,分配给客户端,就不会再有这个原型实例更多的记录了。因此,尽管所有不分范围的对象都会调用初始化生命周期回调方法,对于原型bean,注册的生命周期的销毁阶段是不会被调用的。客户端代码必须清除原型范围(prototype-scoped)的对象并且释放那些被prototype Bean保持的昂贵的资源。希望Spring容器来释放被原型bean保持的对象,试一下使用自定义bean后处理(custom bean post-processor),这个后处理保持了需要被清理bean的引用。
new
操作符可以互换的。所有过了生命周期管理以后,都需要由客户端来管理。(更多关于Spring
Bean的声明周期细节,请参考6.6.1节“生命周期回调”( Section 6.6.1,
“Lifecycle callbacks”))request
, session
和global
session
范围只有在你用到基于web的Spring ApplicationContext
实现的时候才会有用(例如XmlWebApplicationContext)。如果你在普通的Spring
Ioc容器中使用这些范围,例如 XmlWebApplicationContext
,你会得到一个报出一个未命名(unknown
bean scope)的Bean范围的IllegalStateException
错误。request
, session
和global
session
级别的Bean范围(web范围的bean),在你定义Bean之前需要做一些小的初始化配置。(这个配置对标准范围是不需要的,例如 singleton
和prototype
)。DispatcherServlet
或 DispatcherPortlet
来处理请求,那么什么都不用做:DispatcherServlet
和 DispatcherPortlet
已经了解了所有有关的状态。DispatcherServlet
之外处理request(例如,当使用JSF或Struts),你需要注册org.springframework.web.context.request.RequestContextListener的ServletRequestListener
。对于Servlet
3.0+,这个可以通过WebApplicationInitializer
自动完成。另外,对于更老的容器,给你应用 web.xml
文件增加如下声明:<web-app> ... <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> ... </web-app>另外,如果你的监听启动有问题,可以考虑使用Spring的
RequestContextFilter
。过滤器的映射取决于包围的Web容器的配置,所以你需要作出适当的改变。<web-app> ... <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> ... </web-app>
DispatcherServlet
, RequestContextListener
,
和 RequestContextFilter
都做的是同样的事情,绑定HTTP请求对象到请求服务的线程,这可以进一步下降请求传递链使request-
and session-范围的Bean可用。考虑下面的Bean定义:
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>Spring容器通过使用
loginAction
Bean的定义来为每一个HTTP请求创建一个新的 loginAction
Bean的实例。也就是, loginAction
Bean的范围是在HTTP请求(request)级别的。你可以如你所愿的更改创建的实例的内部状态,因为其他的由相同 loginAction
Bean配置创建的实例不会看到这个状态的改变;他们对于单独的请求是唯一的。当请求完成处理,请求范围的Bean实例就会被丢弃。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>Spring容器使用
UserPreferences
Bean定义为每一个HTTP Session
生命周期内创建一个新的UserPreferences
Bean的实例。换句话说,UserPreferences
Bean在HTTP Session
级别才有效。正如userPreferences
Bean,你可以如你所愿的改变这些创建的实例的状态,其他的HTPP Session
实例也会使用相同Bean定义创建的实例,这些实例是互相看不到对方状态的,因为他们对于每一个HTTP Session
都是唯一的。当HTTP Session
被丢弃的时候,相应范围的Bean也会被丢弃。<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
global
session
范围和标准的HTTP Session
范围相似,只在基于Portlet( portlet-based)的Web应用中在可用。Portlet说明书定义global Session
的概念是,由所有组成portlet的单一的portlet
web应用所共享。被定义为global
session
范围的Bean在全局portlet Session
生命周期中都是有效的。global
session
范围,容器会使用标准的HTTPSession
范围,不会报错。<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>Spring容器为整个web应用只会创建一个
AppPreferences
实例。也就是, AppPreferences
范围是ServletContext
级别的,存入为一个普通的ServletContext
属性。这有些和Spring的singleton相似,但是两点主要的不同:它在每个ServletContext
中是唯一的,而不是每个‘ApplicationContext’(或者在给定的应用中可能有多个实例),而且它实际上是作为ServletContext
的参数暴漏出来的。Spring容器不仅管理你的对象(Bean)的初始化,而且连接他们之间的协作关系(或依赖)。如果你 想把一个HTTP request范围的Bean注入到另一个拥有更长的范围的Bean,你应该选择注入AOP代理而不是范围Bean。也就是,你需要注入一个和范围Bean暴露相同公共接口的代理对象,这个代理可以给实际的目标对象映射一个相同范围(例如HTTP request范围),代理实际对象的方法调用。
你还可以在
并且,范围代理不仅仅是唯一用一种生命周期安全的方式(lifecycle-safe
fashion)在短生命周期的bean中访问长声明周期bean。你也可以把你的注入点(像构造方法,setter方法或者是自动装配字段)声明为 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- an HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <!-- instructs the container to proxy the surrounding bean --> <aop:scoped-proxy/> </bean> <!-- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.foo.SimpleUserService"> <!-- a reference to the proxied userPreferences bean --> <property name="userPreferences" ref="userPreferences"/> </bean> </beans>
<aop:scoped-proxy/>
元素。参考“选择创建代理的类型”章节和第40章基于XML模式的配置。为什么request
, session
,globalSession
和自定义范围的bean需要<aop:scoped-proxy/>
元素?让我们解释下面的singleton
Bean定义,与你上面提到的范围做对比。(下面的userPreferences
Bean定义按目前来说还没有完成)。<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>在上面的例子中,singleton Bean
userManager
被注入了一个HTTPSession
范围的userPreferences
Bean引用。这里需要注意的点是userManager
Bean是singleton的。它只会在每一个容器里被实例化一次,它的依赖(在这里仅仅有一个userPreferences
Bean)仅仅被注入了一次。这意味着 userManager
Bean仅仅对相同的userPreferences
对象进行操作,也就是,最开始注入的哪一个。Session
范围的Bean注入到一个singleton
Bean中。而对于在特定的HTTP Session
生命周期期间你需要一个userPreferences
对象。因此容器创建了一个暴露相同公共接口的对象作为 UserPreferences
类(理想情况下是一个 UserPreferences
实例对象),这个类可以从范围界定机制(the
scoping mechanism)下获取真正的UserPreferences
对象。容器给userManager
bean注入了一个代理的对象,但你无法意识到这是一个代理。在这个例子中,当UserManager
实例调用了依赖注入的UserPreferences
实例的方法,它实际上调用了代理对象的方法。然后代理再从HTTPSession
中获取实际的UserPreferences
对象,并且通过代理方法调用实际对象。request-
或session-
Bean给协作的对象时。<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <aop:scoped-proxy/> </bean> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>
<aop:scoped-proxy/>
元素的Bean的代理的时候,一个基于CGLIIB类的(CGLIB-based
class)代理就会被创建。
CGLIB代理仅仅拦截公共方法调用!不会调用代理的非public方法;他们也不会代理到实际的范围目标对象上。 |
false
。用基于JDK的代理意味着你不需要在你的classpath中增加额外的类库来产生代理。但是,它也意味着你的范围Bean类必须实现至少一个接口,你所有要注入的Bean都需要通过它的接口。<!-- DefaultUserPreferences implements the UserPreferences interface --> <bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/> </bean> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>了解更多选择基于类或基于接口代理的信息,看10.6章“代理机制”(Section 10.6, “Proxying mechanisms”)。
bean的范围机制是可拓展的;你可以定义你自己的范围,或甚至重新定义存在的范围,尽管后者认为是不好的习惯,并且你不能重写内建的singleton
prototype
范围。
org.springframework.beans.factory.config.Scope
接口,这个接口将在本节讲解。对于如何实现你自己的范围,参考Spring容器自身的定义的范围实现和 Scope
JAVA文档,那里讲解了你需要实现的一些细节。Scope
接口有四个方法从范围内获取对象,删除它们和允许它们销毁。Object get(String name, ObjectFactory objectFactory)
Object remove(String name)
void registerDestructionCallback(String name, Runnable destructionCallback)
String getConversationId()
Scope
实现以后,你需要让Spring知道你的范围存在。下面的方法是注册一个新的范围的核心方法:void registerScope(String scopeName, Scope scope);
这个方法声明在ConfigurableBeanFactory
接口,它在大多通过BeanFactory装配的Spring的ApplicationContext
实现都可用。
方法的第一个参数适合范围关联的唯一的名字;例如Spring框架自己的 singleton
和 prototype
范围。第二个方法参数是你希望注册和使用的实际自定范围的实例。
假设你写了下面的自定义 Scope
实现,并且像下面一样注册了它。
下面的例子用了Spring包含的SimpleThreadScope例子,但是不是默认注册的。结构应该和你自己定义的 |
Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope);
你然后通过你自定义范围规则创建一个bean的定义:
<bean id="..." class="..." scope="thread">
对于你自己的 Scope
实现,你不仅可以在程序里注册,你也可以通过CustomScopeConfigurer
类注册:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean> <bean id="bar" class="x.y.Bar" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="foo" class="x.y.Foo"> <property name="bar" ref="bar"/> </bean> </beans>
当你把<aop:scoped-proxy/>放到 |
标签:
原文地址:http://blog.csdn.net/z1049186181/article/details/51451019