参考:官方文档

1 Bean的作用域

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

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

ScopeDescription
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作用域是如何工作的:

在这里插入图片描述

上图中,有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作用域是如何工作的:

在这里插入图片描述

上图中,每个引用对应一个新的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作为依赖项注入

没太看懂,详见官方文档

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐