深入解读Spring Framework IoC容器(第六弹:Bean的作用域)

本文详细介绍了Spring框架中Bean的各种作用域,包括singleton、prototype、request等,并解释了它们的应用场景及配置方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bean作用域(scope)有如下种类:

ScopeDescription
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支持我们自定义作用域,不过一般不建议自定义,所以这里暂时先不多说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值