引言

    在Java编程中spring框架已经使用的非常广泛,如今由spring框架派生的spring周边项目也逐渐流行起来,如spring boot,在现在的微服务里面用的甚多。但是用了这么多spring框架的模块和项目,有些时候还是需要对spring框架的基本知识进行深入了解,这里我讲记录下spring框架中bean的作用域。  
    在Spring框架中,可以在6个内置的范围中创建bean,也可以定义自定义范围。 在这六个范围中,只有在使用Web感知的ApplicationContext时才有四个范围可用。singleton 和prototype (单例和多例)作用域 在任何的spring框架IOC容器都可用。

一.spring框架中Bean的作用域类型讲解

下面是六个spring框架中默认可用的作用域
作用域类型 简介
singleton(默认单例) spring框架的 IoC 容器中只有单个实例。
prototype 与单例相反, 多例会在每次使用他的时候创建一个新的实例.
request 在HTTP request的完整生命周期中,将创建并提供一个实例。

只在web感知的Spring ApplicationContext中有效。

session 在HTTP session的整个生命周期中,将创建一个单一的实例并使其可用。

只在web感知的Spring ApplicationContext中有效。

application 在ServletContext的完整生命周期中,将创建并提供一个实例。

只在web感知的Spring ApplicationContext中有效。

websocket WebSocket的完整生命周期中将创建并提供一个实例。

Only valid in web-aware Spring ApplicationContext.

1.1 spring框架singleton(单例) scope(作用域)

    singleton 是spring容器中的默认bean作用域。 它告诉容器创建和管理每个容器只有一个bean类的实例。 这个单实例存储在这样的单例bean的缓存中,并且该命名bean的所有后续请求和引用都会返回缓存的实例。

下面是一个单例作用域,使用的Java代码配置方式:
@Component
@Scope("singleton")//这个语句其实是多余的 - 单例是默认范围
public class BeanClass {
 
}
同样,下面是一个基于xml配置的方式:
<!-- 指定单例作用域是多余的 -->
<bean id="beanId" class="com.leftso.BeanClass" scope="singleton" />
//或者
<bean id="beanId" class="com.leftso.BeanClass" />

1.2 spring框架prototype(多例)scope(作用域)

    prototype (多例)作用域,每一次请求使用到该bean都会被应用创建一个新的实例。

    你应该知道销毁bean的生命周期方法并不会调用prototype 作用域的beans,只有在初始化回调方法的时候调用。因此作为开发人员,应该负责清理prototype 作用域的beans实例和他们挂着的其他资源。

Java代码配置的方式的prototype(多例)作用域例子:
@Component
@Scope("prototype")
public class BeanClass {
}
下面是xml的方式:
<bean id="beanId" class="com.leftso.BeanClass" scope="singleton" />

提示:通常应该使用prototype(多例)在有状态的bean ,singleton(单例)处理无状态的bean

注意:在request, session, application 和 websocket 作用域中使用beans,必须注册RequestContextListener或者RequestContextFilter
 

1.3 spring框架request scope(作用域)

    在request 范围中,容器为每个HTTP请求创建一个新的实例。因此,如果服务器正在处理50个请求,然而容器最多可以创建50个单独bean的实例。 任何状态更改为一个实例,其他实例将不可见。 这些实例一旦请求完成就被销毁。

Java代码配置方式例子:
@Component
@Scope("request")
public class BeanClass {
}
 
//或者
 
@Component
@RequestScope
public class BeanClass {
}
xml配置例子:
<bean id="beanId" class="com.leftso.BeanClass" scope="request" />

1.4 spring框架session scope(作用域)

    在session 作用域,容器为每个HTTP会话创建一个新的实例。因此,如果服务器有20个活动会话,并且容器最多可以有20个单独的bean类实例。在单个会话生命周期内的所有HTTP请求将有权访问该会话作用域中相同的单个bean实例。
    任何状态更改为一个实例,其他实例将不可见。 一旦会话被销毁/终止在服务器上,这些实例就被销毁了。

Java代码配置例子:
@Component
@Scope("session")
public class BeanClass {
}
 
//or
 
@Component
@SessionScope
public class BeanClass {
}
xml配置例子:
<bean id="beanId" class="com.leftso.BeanClass" scope="session" />

1.5 spring框架application scope(作用域)

   在application 作用域中,容器每个Web应用程序运行时创建一个实例 它几乎类似于单一的范围,只有两个不同之处,即:
  1. application 范围的bean是每个ServletContext 单例,而singleton 范围的bean是每个ApplicationContext单例。 请注意,单个应用程序可以有多个应用程序上下文。
  2. application 范围的bean作为一个ServletContext 属性是可见的。
java代码配置例子:
@Component
@Scope("application")
public class BeanClass {
}
 
//or
 
@Component
@ApplicationScope
public class BeanClass {
}
xml配置例子:
<bean id="beanId" class="com.leftos.BeanClass" scope="application" />

1.6 spring框架websocket  scope(作用域)

    WebSocket协议启用客户端和选择与客户端通信的远程主机之间的双向通信。 WebSocket协议为两个方向上的流量提供单个TCP连接。 这对于同时编辑和多用户游戏的多用户应用程序特别有用。

    在这种类型的Web应用程序中,HTTP仅用于初始握手。 服务器可以响应HTTP状态101(切换协议),如果它同意 - 握手请求。 如果握手成功,则TCP套接字保持打开状态,客户端和服务器都可以使用它来相互发送消息。

java代码配置例子:
@Component
@Scope("websocket")
public class BeanClass {
}
xml配置例子:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" />
请注意,websocket范围的bean通常是单身,比任何单独的WebSocket会话寿命更长。
 

1.7当前线程作用域

  Spring框架还提供了一个使用SimpleThreadScope类的非默认thread 作用域。 要使用此范围,您必须使用CustomScopeConfigurer 类将其注册到容器。
<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的请求都会在同一个线程中返回相同的实例。

Java配置例子:
@Component
@Scope("thread")
public class BeanClass {
}
xml配置例子:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="thread" />

二.总结

    Spring框架提供了六个bean作用域,每个作用域内的实例有不同的生命周期跨度。 作为开发人员,我们必须明智地选择任何容器管理bean的范围。 另外,当不同范围的豆子互相引用时,我们必须做出明智的选择。