Bean的作用域

​ 创建bean定义时,将创建一个配方,用于创建该bean定义所定义的类的实际实例。 bean定义是配方的想法很重要,因为这意味着与类一样,您可以从一个配方中创建许多对象实例。

​ 您不仅可以控制要插入到从特定bean定义创建的对象中的各种依赖项和配置值,还可以控制从特定bean定义创建的对象的范围。这种方法功能强大且灵活,因为您可以选择通过配置创建的对象的范围,而不必在Java类级别上烘烤对象的范围。可以将Bean定义为部署在多个范围之一中。 Spring框架支持六个范围,其中只有在使用Web感知的ApplicationContext时才可用。您还可以创建自定义范围。

​ 下表描述了受支持的范围:

作用域描述
singleton(默认)将每个Spring IoC容器的单个bean定义范围限定为单个对象实例。
prototype将单个bean定义的作用域限定为任意数量的对象实例。
request将单个bean定义的范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个自己的bean实例,它是在单个bean定义的后面创建的。仅在可感知网络的Spring ApplicationContext上下文中有效。
session将单个bean定义的作用域限定为HTTP会话的生命周期。仅在可感知网络的Spring ApplicationContext上下文中有效。
application将单个bean定义的作用域限定为ServletContext的生命周期。仅在可感知网络的Spring ApplicationContext上下文中有效。
websocket将单个bean定义的作用域限定为WebSocket的生命周期。仅在可感知网络的Spring ApplicationContext上下文中有效。

​ 从Spring 3.0开始,线程作用域可用,但默认情况下未注册。有关更多信息,请参见SimpleThreadScope文档。

1.5.1 Singleton 单例作用域

仅管理一个singleton bean的一个共享实例,并且所有对具有ID或与该bean定义相匹配的ID的bean的请求都将导致该特定的bean实例由Spring容器返回。

换句话说,当您定义一个bean定义并且其作用域为单例时,Spring IoC容器将为该bean定义所定义的对象创建一个实例。该单个实例存储在此类单例bean的高速缓存中,并且对该命名bean的所有后续请求和引用都返回该高速缓存的对象。下图显示了单例作用域的工作方式:

在这里插入图片描述

最好将Spring单例的范围描述为每个容器和每个bean。这意味着,如果您在单个Spring容器中为特定类定义一个bean,则Spring容器将创建该bean定义所定义的类的一个实例,并且只有一个实例。单例作用域是Spring中的默认作用域。要将bean定义为XML中的单例,可以定义bean,如以下示例所示:

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

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
1.5.2 Prototype 原型作用域

每次对特定bean提出请求时,bean部署的非单一原型范围都会导致创建一个新bean实例。也就是说,该Bean被注入到另一个Bean中,或者您可以通过容器上的getBean()方法调用来请求它。通常,应将原型作用域用于所有有状态Bean,将单例作用域用于无状态Bean。

下图说明了Spring原型范围:

在这里插入图片描述

(数据访问对象(DAO)通常不配置为原型,因为典型的DAO不拥有任何对话状态。对于我们而言,重用单例图的核心更为容易。)

以下示例将bean定义为XML原型:

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

与其他作用域相反,Spring不管理原型Bean的完整生命周期。容器实例化,配置或组装原型对象,然后将其交给客户端,而无需对该原型实例的进一步记录。因此,尽管在不考虑范围的情况下在所有对象上都调用了初始化生命周期回调方法,但对于原型,则不会调用已配置的销毁生命周期回调。客户端代码必须清除原型作用域内的对象,并释放原型Bean拥有的昂贵资源。为了使Spring容器释放原型作用下的bean所拥有的资源,请尝试使用自定义bean后处理器,该处理器包含对需要清理的bean的引用。

在某些方面,Spring容器在原型作用域Bean方面的角色是Java new运算符的替代。超过该时间点的所有生命周期管理必须由客户端处理。

1.5.3 具有原型Bean依赖关系的Singleton Bean

当您使用对原型bean有依赖性的单例作用域Bean时,请注意,依赖关系在实例化时已解决。因此,如果将依赖项原型的bean依赖项注入到单例范围的bean中,则将实例化新的原型bean,然后将依赖项注入到单例bean中。原型实例是曾经提供给单例范围的bean的唯一实例。

但是,假设您希望单例作用域的bean在运行时重复获取原型作用域的bean的新实例。您不能将原型作用域的bean依赖项注入到您的单例bean中,因为当Spring容器实例化单例bean并解析并注入其依赖项时,该注入仅发生一次。如果在运行时不止一次需要原型bean的新实例,请参见方法注入。

1.5.4 Request,Session,Application,WebSocket 作用域

仅当您使用可感知Web的Spring ApplicationContext实现(例如XmlWebApplicationContext)时,请求,会话,应用程序和websocket范围才可用。如果将这些作用域与常规的Spring IoC容器(例如ClassPathXmlApplicationContext)一起使用,则会抛出一个IllegalStateException异常,该错误抱怨未知的bean作用域。

  • 初始Web配置

    为了支持在request,session,application, andwebsocket级别(Web范围的Bean)的Bean范围界定,在定义Bean之前,需要一些较小的初始配置。 (对于标准示波器:单例和原型,不需要此初始设置。)

    如何完成此初始设置取决于您的特定Servlet环境。

    实际上,如果您在Spring Web MVC中访问由Spring DispatcherServlet处理的请求中的作用域Bean,则不需要特殊的设置。 DispatcherServlet已经公开了所有相关状态。

    如果您使用Servlet 2.5 Web容器,并且在Spring的DispatcherServlet之外处理请求(例如,使用JSF或Struts时),则需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于Servlet 3.0+,可以使用WebApplicationInitializer接口以编程方式完成此操作。或者,或者对于较旧的容器,将以下声明添加到Web应用程序的web.xml文件中:

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

    另外,如果您的监听器设置存在问题,请考虑使用Spring的RequestContextFilter。过滤器映射取决于周围的Web应用程序配置,因此您必须适当地对其进行更改。以下清单显示了Web应用程序的过滤器部分:

    <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请求对象绑定到为该请求提供服务的Thread。这使得在请求链和会话范围内的bean可以在调用链的更下游使用。

  • request作用域

    考虑以下XML配置来定义bean:

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

    Spring容器通过为每个HTTP请求使用loginAction bean定义来创建LoginAction bean的新实例。也就是说,loginAction bean的作用域为HTTP请求级别。您可以根据需要更改创建实例的内部状态,因为从同一loginAction bean定义创建的其他实例看不到这些状态更改。它们特定于单个请求。当请求完成处理时,将限制作用于该请求的Bean。

    当使用注释驱动的组件或Java配置时,@ RequestScope注释可用于将组件分配给请求范围。以下示例显示了如何执行此操作:

    @RequestScope
    @Component
    public class LoginAction {
        // ...
    }
    
  • session作用域

    考虑以下XML配置来定义bean:

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

    Spring容器通过在单个HTTP会话的生存期内使用userPreferences bean定义来创建UserPreferences bean的新实例。换句话说,userPreferences bean有效地作用在HTTP会话级别。与使用请求范围的Bean一样,您可以根据需要任意更改所创建实例的内部状态,因为知道其他也在使用从同一userPreferences Bean定义创建的实例的HTTP Session实例也看不到这些状态变化,因为它们特定于单个HTTP会话。当HTTP会话最终被丢弃时,作用于该特定HTTP会话的bean也将被丢弃。

    使用注释驱动的组件或Java配置时,可以使用@SessionScope注释将组件分配给会话范围。

    @SessionScope
    @Component
    public class UserPreferences {
        // ...
    }
    
  • application作用域

    考虑以下XML配置来定义bean:

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

    Spring容器通过对整个Web应用程序使用一次appPreferences bean定义来创建AppPreferences bean的新实例。也就是说,appPreferences bean的作用域位于ServletContext级别,并存储为常规ServletContext属性。这有点类似于Spring单例bean,但是有两个重要的区别:它是每个ServletContext的单例,而不是每个Spring’ApplicationContext’的单例(在任何给定的Web应用程序中可能都有多个),并且实际上是公开的,因此可见为ServletContext属性。

    使用注释驱动的组件或Java配置时,可以使用@ApplicationScope注释将组件分配给应用程序范围。以下示例显示了如何执行此操作:

    @ApplicationScope
    @Component
    public class AppPreferences {
        // ...
    }
    
1.5.5 自定义作用域

​ Bean作用域机制是可扩展的。您可以定义自己的范围,甚至重新定义现有的范围,尽管后者被认为是不好的做法,并且您不能覆盖内置的singleton和原型范围。

​ 要将自定义范围集成到Spring容器中,需要实现org.springframework.beans.factory.config.Scope接口。

参考文献

【https://docs.spring.io/spring-framework/docs/current/reference/html/core.html】【1.5. Bean Scopes】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值