1 Bean的作用域

Spring Framework中,总共定义了6种bean 的作用域,其中有4种作用域只有当应用为web应用的时候才有效,并且Spring还支持自定义作用域。

下表描述了这6种作用域:

Scope

Description

singleton

(默认的)在每个Spring IoC容器中,一个bean定义对应只会有唯一的一个bean实例。

prototype

一个bean定义可以有多个bean实例。

request

一个bean定义对应于单个HTTP 请求的生命周期。也就是说,每个HTTP 请求都有一个bean实例,且该实例仅在这个HTTP 请求的生命周期里有效。该作用域仅适用于WebApplicationContext环境。

session

一个bean 定义对应于单个HTTP Session 的生命周期,也就是说,每个HTTP Session 都有一个bean实例,且该实例仅在这个HTTP Session 的生命周期里有效。该作用域仅适用于WebApplicationContext环境。

application

一个bean 定义对应于单个ServletContext 的生命周期。该作用域仅适用于WebApplicationContext环境。

websocket

一个bean 定义对应于单个websocket 的生命周期。该作用域仅适用于WebApplicationContext环境。

1.1 singleton作用域

singleton作用域表示在整个Spring容器中一个bean定义只生成了唯一的一个bean实例,被Spring容器管理。所有对这个bean的请求和引用都会返回这个bean实例。

下面的图说明了singleton作用域是如何工作的:

Javaspringboot bean是什么 spring boot bean的作用_HTTP

上图中,有3个地方引用了这个bean,这3个引用指向的都是同一个bean实例。

singleton作用域是Spring中默认的作用域,可以在定义bean的时候指定或者不指定都可以,如下:

<!-- 不指定作用域,默认是singleton -->
<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- 显示指定作用域为singleton -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

1.2 prototype作用域

prototype作用域表示的是一个bean定义可以创建多个bean实例,有点像一个类可以new多个实例一样。

也就是说,当注入到其他的bean中或者对这个bean定义调用getBean()时,都会生成一个新的bean实例。

作为规则,应该对所有有状态的bean指定prototype作用域,对所有无状态的bean指定singleton作用域。

下图描述了prototype作用域是如何工作的:

Javaspringboot bean是什么 spring boot bean的作用_HTTP_02

上图中,每个引用对应一个新的bean实例。

请注意,上图中的例子不适用于生产环境。因为DAO通常来说是无状态的bean,应该指定它的作用域为singleton比较合适。

xml中可以这样定义prototype作用域:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

和其他作用域不同的是,Spring并不管理作用域为prototypebean的整个生命周期。Spring容器实例化它、配置它、组装它,然后就将bean交给给使用者了,之后就不会对这个bean进行管理了。因此,Spring不会调用该bean的销毁生命周期回调,使用者必须自己销毁 这个bean并释放资源。如果想让Spring来销毁它并释放资源,请使用自定义的bean post-processor

1.3 当singleton的bean依赖prototype的bean

singletonbean依赖prototypebean时,请注意,这个依赖关系是在实例化时候解析的,并且只解析一次。因此,每个依赖的prototypebean都是一个新的bean实例。

然而,如果一个singletonbean想要在运行时,在每次注入时都能有一个新的prototypebean生成并注入,这是不行的。因为依赖注入在初始化的时候只会注入一次。如果想要在运行时多次注入新的prototypebean,请参考Method Injection

1.4 request、session、application、websocket作用域

requestsessionapplicationwebsocket作用域只有在web环境下才有用。

1.4.1 初始化web配置

为了支持requestsessionapplicationwebsocket作用域,在定义bean之前需要一些初始化配置。

如何完成这个初始化配置取决于你的特定Servlet环境。

如果你在Spring Web MVC中定义这4中作用域,不需要进行初始化配置。因为,Spring使用的DispatcherServlet已经公开了所有相关的状态。

如果你使用的是Servlet 2.5web容器,这些容器不使用SpringDispatcherServlet,你需要注册org.springframework.web.context.request.RequestContextListener。对于Servlet 3.0+的web容器,可以通过编程的方式使用WebApplicationInitializer接口来完成,或者使用xml配置:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

如果监听器设置遇到问题,可以考虑配置SpringRequestContextFilter

<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>

DispatcherServletRequestContextListenerRequestContextFilter 共同做了相同的事,即将HTTP请求对象绑定到服务该请求的线程。这使得请求作用域和会话作用域的bean在调用链的更下游可用。

1.4.2 request作用域

下面的xml配置指定了bean的作用域为request

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

Spring为每一个HTTP请求创建一个新的LoginActionbean。每个bean之间不可见,当请求结束,对应的bean将被丢弃。

当使用注解定义bean的时候,可以通过使用@RequestScope注解设定request作用域:

@RequestScope
@Component
public class LoginAction {
    // ...
}

1.4.3 session作用域

xml中可以这样定义bean的作用域为session作用域:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

Spring为每个HTTP Session创建一个新的UserPreferencesbean。每个bean之间是不可见的,只属于对应的Session,当Session销毁的时候,对应的bean也会被销毁。

当使用注解定义bean的时候,可以通过@SessionScope注解设定session作用域:

@SessionScope
@Component
public class UserPreferences {
    // ...
}

1.4.4 application作用域

xml中可以这样定义bean的作用域为application作用域:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

Spring为整个web上下文创建一个AppPreferencesbean。也就是说,这个bean作用域是在ServletContext级别,它是作为ServletContext的一个属性存在的。

这和singleton作用域很像,但是有两点不同:

  • application作用域的bean是每个ServletContext只有一个;而singleton作用域是每个SpringApplicationContext只有一个。两者的范围不同。
  • application作用域的bean是属于ServletContext的,作为ServletContext的属性,是可见的。

当使用注解定义bean的时候,可以通过@ApplicationScope注解设定application作用域:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}

1.4.5 被设定了作用域的bean作为依赖项注入