Bean作用域(scope)有如下种类:
Scope | Description |
---|---|
singleton | 这是默认的,每个Spring IoC容器中一个bean定义只对应一个实例。 |
prototype | 一个bean定义对应多个实例。 |
request | 一个bean定义作用于一个HTTP request生命周期。仅在基于web的Spring ApplicationContext中有效。 |
session | 一个bean定义作用于一个HTTP session生命周期。仅在基于web的Spring ApplicationContext中有效。 |
globalSession | 一个bean定义作用于全局的HTTP session生命周期。仅在portlet context中有效。仅在基于web的Spring ApplicationContext中有效。 |
application | 一个bean定义作用于整个ServletContext生命周期。仅在基于web的Spring ApplicationContext中有效。 |
websocket | 一个bean定义作用于整个WebSocket生命周期。仅在基于web的Spring ApplicationContext中有效。 |
单例作用域(singleton scope)
对于一个单例的bean定义,Spring IoC容器仅创建并管理一个共享实例。这个实例被存储在一个包含很多单例bean的缓存中,所有的请求和引用都会返回缓存的对象。
在XML中定义一个单例的bean,需要写如下的配置:
<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- 写不写scope="singleton"其实都一样,因为默认就是单例的 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
原型作用域(prototype scope)
bean使用原型作用域,会在每次请求该bean时,创建一个新的bean实例 。通常,对于有状态的bean使用原型作用域,无状态的bean则使用单例作用域。
与其他作用域不同,Spring容器不会管理原型域bean的完整生命周期。Spring容器初始化原型bean对象,然后交给客户端,之后就不管这个bean对象了。 因此,对于bean的生命周期方法来说,尽管所有作用域的初始化方法都会被调用, 但是原型域bean的销毁方法不会被Spring容器调用。客户端代码要自己负责销毁原型bean。如果想让Spring负责销毁,就得自定义bean的后处理器bean post-processor。
XML中定义原型bean配置如下:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
web相关的作用域
当使用web相关的Spring ApplicationContext(例如 XmlWebApplicationContext)时,request、session和global session作用域才会起作用。如果在普通的Spring IoC 容器(例如 ClassPathXmlApplicationContext)中使用这几个作用域,会抛出异常 IllegalStateException,表示这是一个未知的bean作用域。
要使用这些作用域, 在定义bean之前要做一些最基本的初始化配置。
如果使用的是Spring Web MVC,在DispatcherServlet或DispatcherPortlet处理的请求内去访问这些作用域的bean,那就不用做任何专门的配置,因为DispatcherServlet和DispatcherPortlet已经帮我们做了这些事情。
如果你使用的是 Servlet2.5 标准的web容器,而且请求不是由 Spring的 DispatcherServlet 处理 ,例如使用了Struts,就得注册org.springframework.web.context.request.RequestContextListener这个ServletRequestListener 。在 web.xml 配置文件中加入以下的声明:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
或者不使用listener,使用filter:
<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请求对象绑定到处理线程上,使得web相关作用域的bean可以沿着调用链往下走。
请求作用域(request scope)
bean定义配置如下:
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
对于每个http请求,Spring容器会创建一个loginAction bean的新实例。我们可以在请求内随意修改这个bean实例的状态,因为其他LoginAction bean实例看不到这些变化。当请求处理完毕,对应的bean实例也就销毁了。
当使用注解驱动或者Java配置时,可以使用@RequestScope注解来把一个component声明成请求作用域。
@RequestScope
@Component
public class LoginAction {
// ...
}
会话作用域(session scope)
bean定义配置如下:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
在每个HTTP Session的生命周期内,Spring容器会创建一个userPreferences bean的新实例。和请求作用域bean类似, 因为每个会话域bean的范围限于特定的HTTP Session内部,所以一个Session内的userPreferences bean也是可以被随意修改,而不会影响到其他Session中的userPreferences bean。当一个HTTP Session用完被回收时,相关的会话域bean也被一起回收。
当使用注解驱动或者Java配置时,可以使用@SessionScope注解来把一个component声明成会话作用域。
@SessionScope
@Component
public class UserPreferences {
// ...
}
全局会话作用域(global session scope)
bean定义配置如下:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
全局会话作用域与HTTP Session作用域类似,但是仅适用于基于门户(portlet)的web应用程序上下文。
应用程序作用域(application scope)
bean定义配置如下:
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring容器在整个web应用启动时创建了一个AppPreferences bean的实例。这跟Spring的单例作用域有些相似,但有两个重大差异:
1. 这是某个ServletContext的单例,不是某个Spring ApplicationContext的单例。任何web应用都可能有多个ApplicationContext。
2. 这是作为一个ServletContext属性被暴露和访问的。
当使用注解驱动或者Java配置时,可以使用@ApplicationScope注解来把一个component声明成应用程序作用域。
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
自定义作用域
Spring支持我们自定义作用域,不过一般不建议自定义,所以这里暂时先不多说。