spring Bean范围

213 阅读7分钟

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

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

下表描述了支持的范围:

范围描述
独生子(默认)将单个bean定义范围限定为每个Spring IoC容器的单个对象实例。
原型将单个bean定义范围限定为任意数量的对象实例。
请求将单个bean定义范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。仅在具有Web感知功能的Spring环境中有效ApplicationContext
会议将单个bean定义范围限定为HTTP的生命周期Session。仅在具有Web感知功能的Spring环境中有效ApplicationContext
应用将单个bean定义范围限定为a的生命周期ServletContext。仅在具有Web感知功能的Spring环境中有效ApplicationContext
WebSocket将单个bean定义范围限定为a的生命周期WebSocket。仅在具有Web感知功能的Spring环境中有效ApplicationContext

单身范围

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

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

独生子

Spring的单例bean概念不同于Gang of Four(GoF)模式书中定义的单例模式。GoF单例对一个对象的范围进行硬编码,使得每个ClassLoader创建一个且只有一个特定类的实例。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"/>

原型范围

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运算符的替代品。超过该点的所有生命周期管理必须由客户端处理。(有关Spring容器中bean的生命周期的详细信息)

 

自定义范围

bean范围机制是可扩展的。您可以定义自己的范围,甚至可以重新定义现有范围,尽管后者被认为是不好的做法,您无法覆盖内置singletonprototype范围。

创建自定义范围

要将自定义作用域集成到Spring容器中,需要实现org.springframework.beans.factory.config.Scope本节中描述的 接口。有关如何实现自己的作用域的想法,请参阅Scope Spring Framework本身和Scopejavadoc 提供的实现 ,它们解释了您需要更详细地实现的方法。

Scope接口有四种方法可以从作用域中获取对象,将其从作用域中删除,然后将其销毁。

例如,会话范围实现返回会话范围的bean(如果它不存在,则该方法在将其绑定到会话以供将来参考之后返回该bean的新实例)。以下方法从基础范围返回对象:

Object get(String name, ObjectFactory objectFactory)

例如,会话范围实现从基础会话中删除会话范围的bean。应返回该对象,但如果找不到具有指定名称的对象,则可以返回null。以下方法从基础范围中删除对象:

Object remove(String name)

以下方法记录范围在销毁时或范围中指定对象被销毁时应执行的回调:

void registerDestructionCallback(String name, Runnable destructionCallback)

有关 销毁回调的更多信息,请参阅javadoc或Spring作用域实现。

以下方法获取基础范围的对话标识符:

String getConversationId()

每个范围的标识符都不同。对于会话范围的实现,该标识符可以是会话标识符。

使用自定义范围

在编写并测试一个或多个自定义Scope实现之后,需要让Spring容器知道您的新范围。以下方法是Scope使用Spring容器注册new的核心方法:

void registerScope(String scopeName, Scope scope);

此方法在ConfigurableBeanFactory接口上声明,该接口可通过 Spring随附的BeanFactory大多数具体ApplicationContext实现的属性获得。

registerScope(..)方法的第一个参数是与范围关联的唯一名称。Spring容器本身中的这些名称的示例是singleton和 prototype。该registerScope(..)方法的第二个参数是Scope您希望注册和使用的自定义实现的实际实例。

假设您编写自定义Scope实现,然后注册它,如下一个示例所示。

 下一个示例使用SimpleThreadScope,它包含在Spring中,但默认情况下未注册。您自己的自定义Scope 实现的说明是相同的。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

然后,您可以创建符合自定义的作用域规则的bean定义, Scope如下所示:

<bean id="..." class="..." scope="thread">

使用自定义Scope实现,您不仅限于范围的编程注册。您还可以Scope使用CustomScopeConfigurer该类以声明方式进行注册 ,如以下示例所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <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 id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>
 放置<aop:scoped-proxy/>FactoryBean实现中时,工厂bean本身是作用域的,而不是从中返回的对象getObject()