此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cadn.net.cn

Bean 作用域

创建 Bean 定义时,将创建用于创建实际实例的配方 由该 Bean 定义定义的类。bean 定义是 recipe 很重要,因为它意味着,与 class 一样,您可以创建多个对象 实例。spring-doc.cadn.net.cn

您不仅可以控制各种依赖项和配置值,这些依赖项和配置值 插入到从特定 bean 定义创建的对象中,但也插入到控件 从特定 Bean 定义创建的对象的范围。这种方法是 功能强大且灵活,因为您可以选择所创建对象的范围 通过配置,而不必在 Java 类级别。可以将 Bean 定义为部署在多个范围之一中。 Spring 框架支持六个范围,其中四个范围仅在 您使用 Web 感知ApplicationContext.您还可以创建自定义范围。spring-doc.cadn.net.cn

下表描述了支持的范围:spring-doc.cadn.net.cn

表 1.Bean 作用域
范围 描述

单身 人士spring-doc.cadn.net.cn

(默认)将单个 bean 定义的范围限定为每个 Spring IoC 的单个对象实例 容器。spring-doc.cadn.net.cn

原型spring-doc.cadn.net.cn

将单个 Bean 定义的范围限定为任意数量的对象实例。spring-doc.cadn.net.cn

请求spring-doc.cadn.net.cn

将单个 Bean 定义的范围限定为单个 HTTP 请求的生命周期。那是 每个 HTTP 请求都有自己的 bean 实例,该实例是从单个 bean 的背面创建的 定义。仅在 Web 感知 Spring 的上下文中有效ApplicationContext.spring-doc.cadn.net.cn

会期spring-doc.cadn.net.cn

将单个 bean 定义的范围限定为 HTTP 的生命周期Session.仅在 Web 感知 Spring 的上下文ApplicationContext.spring-doc.cadn.net.cn

应用spring-doc.cadn.net.cn

将单个 bean 定义的范围限定为ServletContext.仅在 Web 感知 Spring 的上下文ApplicationContext.spring-doc.cadn.net.cn

WebSocket 浏览器spring-doc.cadn.net.cn

将单个 bean 定义的范围限定为WebSocket.仅在 Web 感知 Spring 的上下文ApplicationContext.spring-doc.cadn.net.cn

线程范围可用,但默认情况下未注册。有关更多信息, 请参阅以下文档SimpleThreadScope. 有关如何注册此 Scope或任何其他自定义 Scope 的说明,请参阅 Using a Custom Scope

单例范围

仅管理单例 bean 的一个共享实例,以及 bean 的所有请求 替换为与该 Bean 定义的一个或多个 ID 匹配,则生成一个特定的 Bean 实例。spring-doc.cadn.net.cn

换句话说,当你定义一个 bean 定义并且它被定义为 singleton 时,Spring IoC 容器只创建对象的一个实例 由该 bean 定义定义。这个 single 实例存储在 singleton bean 以及该命名 bean 的所有后续请求和引用 返回缓存的对象。下图显示了单一实例范围的工作原理:spring-doc.cadn.net.cn

单身 人士

Spring 的单例 bean 概念不同于 Gang of Four (GoF) 模式书。GoF 单例对 Object 中,以便每个 ClassLoader 的Spring 单例的范围最好描述为每个容器 和每 bean 的 bean 中。这意味着,如果您在 单个 Spring 容器,则 Spring 容器将创建一个且仅创建一个实例 由该 Bean 定义定义的类。singleton scope 是默认范围 在Spring。要在 XML 中将 bean 定义为单例,可以定义一个 bean,如 以下示例:spring-doc.cadn.net.cn

<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 中,或者你通过getBean()方法调用 容器。通常,您应该对所有有状态 bean 使用 prototype 范围,并且 singleton 作用域。spring-doc.cadn.net.cn

下图说明了 Spring 原型范围:spring-doc.cadn.net.cn

原型

(数据访问对象 (DAO) 通常不配置为原型,因为典型的 DAO 不持有 任何对话状态。我们更容易重用 单例图。spring-doc.cadn.net.cn

下面的示例将 Bean 定义为 XML 中的原型:spring-doc.cadn.net.cn

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

与其他范围相比, Spring 不管理 prototype bean 的 bean 中。容器实例化、配置和以其他方式组装 prototype 对象并将其交给客户端,不再记录该原型 实例。因此,尽管初始化生命周期回调方法在所有 对象,无论范围如何,在原型的情况下,配置销毁 生命周期回调。客户端代码必须清理 prototype-scoped 对象,并释放原型 bean 所持有的昂贵资源。要获取 Spring 容器来释放原型范围的 bean 持有的资源,请尝试使用 自定义 bean 后处理器,它保存对需要清理的 bean 的引用。spring-doc.cadn.net.cn

在某些方面, Spring 容器在原型范围的 bean 中的作用是 Java 的替代品new算子。超过该点的所有生命周期管理都必须 由客户端处理。(有关 Spring 中 bean 生命周期的详细信息 容器,请参阅 生命周期回调spring-doc.cadn.net.cn

具有原型 bean 依赖项的单例 bean

当您使用依赖于原型 bean 的单例作用域 bean 时,请注意 依赖项在实例化时解析。因此,如果你依赖注入一个 原型范围的 bean 转换为单例范围的 bean,则会实例化一个新的原型 bean 然后 dependency-injection 到单例 bean 中。prototype 实例是唯一的 实例。spring-doc.cadn.net.cn

但是,假设您希望单例范围的 bean 获取 prototype 范围的 bean。您不能依赖注入 prototype 作用域的 bean 添加到你的单例 bean 中,因为该注入只发生 一次,当 Spring 容器实例化单例 bean 并解析 并注入其依赖项。如果您需要原型 bean 的新实例,位于 运行时,请参阅 Method Injectionspring-doc.cadn.net.cn

请求、会话、应用程序和 WebSocket 范围

request,session,applicationwebsocket范围仅可用 如果使用 Web 感知 SpringApplicationContextimplementation (例如XmlWebApplicationContext).如果你将这些作用域与常规的 Spring IoC 容器一起使用, 例如ClassPathXmlApplicationContextIllegalStateException那个抱怨 关于未知的 bean 范围。spring-doc.cadn.net.cn

初始 Web 配置

为了在request,session,applicationwebsocketlevels(Web 范围的 bean)中,一些次要的初始配置是 在定义 bean 之前是必需的。(此初始设置不是必需的 对于标准范围:singletonprototype.)spring-doc.cadn.net.cn

如何完成此初始设置取决于特定的 Servlet 环境。spring-doc.cadn.net.cn

如果您在 Spring Web MVC 中访问范围限定的 bean,则实际上,在 由 Spring 处理DispatcherServlet,无需特殊设置。DispatcherServletalready 公开所有相关状态。spring-doc.cadn.net.cn

如果您使用 Servlet Web 容器,并且请求在 Spring 的DispatcherServlet(例如,在使用 JSF 时),您需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener. 这可以通过使用WebApplicationInitializer接口。 或者,将以下声明添加到 Web 应用程序的web.xml文件:spring-doc.cadn.net.cn

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

或者,如果您的侦听器设置存在问题,请考虑使用 Spring 的RequestContextFilter.过滤器映射取决于周围的 Web application configuration 的 app 配置,因此您必须根据需要对其进行更改。以下清单 显示了 Web 应用程序的 Filter 部分:spring-doc.cadn.net.cn

<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,RequestContextListenerRequestContextFilter都完全正确 同样的事情,即将 HTTP 请求对象绑定到Thread那就是服务 那个请求。这使得请求和会话范围的 bean 进一步可用 沿着调用链向下。spring-doc.cadn.net.cn

请求范围

对于 Bean 定义,请考虑以下 XML 配置:spring-doc.cadn.net.cn

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

Spring 容器会创建一个LoginActionbean 使用loginActionbean 定义。也就是说,loginActionbean 的范围限定在 HTTP 请求级别。您可以将内部的 state (状态),因为其他实例 从同一loginActionBean 定义在 state 中看不到这些更改。 它们特定于单个请求。当请求完成处理时, 范围限定为请求的 bean 将被丢弃。spring-doc.cadn.net.cn

当使用注解驱动的组件或 Java 配置时,@RequestScope注解 可用于将组件分配给request范围。以下示例显示了如何作 为此,请执行以下作:spring-doc.cadn.net.cn

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

会话范围

对于 Bean 定义,请考虑以下 XML 配置:spring-doc.cadn.net.cn

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

Spring 容器会创建一个UserPreferencesbean 使用userPreferences单个 HTTP 生命周期的 bean 定义Session.在其他 words、userPreferencesbean 的作用域实际上限定在 HTTPSession水平。如 使用请求范围的 bean,您可以更改实例的内部状态,即 根据需要创建尽可能多的 HTTPSession实例也是 使用从同一userPreferencesbean 定义看不到这些 状态更改,因为它们特定于单个 HTTPSession.当 HTTP 协议Session最终被丢弃,则范围限定为该特定 HTTP 的 BeanSession也会被丢弃。spring-doc.cadn.net.cn

当使用注释驱动的组件或 Java 配置时,您可以使用@SessionScope注解将组件分配给session范围。spring-doc.cadn.net.cn

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

应用范围

对于 Bean 定义,请考虑以下 XML 配置:spring-doc.cadn.net.cn

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

Spring 容器会创建一个AppPreferencesbean 使用appPreferencesBean 定义。也就是说,appPreferencesbean 的作用域为ServletContext级别并存储为常规ServletContext属性。这有点类似于 Spring 单例 bean,但 在两个重要方面有所不同:它是ServletContext,而不是每个 SpringApplicationContext(任何给定的 Web 应用程序中都可能有多个), 它实际上是公开的,因此显示为ServletContext属性。spring-doc.cadn.net.cn

当使用注释驱动的组件或 Java 配置时,您可以使用@ApplicationScope注解将组件分配给application范围。这 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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

WebSocket 范围

WebSocket 范围与 WebSocket 会话的生命周期相关联,并应用于 STOMP over WebSocket 应用程序,有关详细信息,请参阅 WebSocket 范围spring-doc.cadn.net.cn

作为依赖项的作用域 Bean

Spring IoC 容器不仅管理对象 (bean) 的实例化, 但也包括合作者(或依赖项)的联系。如果要注入 (对于 example) 将一个 HTTP 请求范围的 bean 转换为另一个寿命较长的 bean 中,您可以 选择注入 AOP 代理来代替作用域 Bean。也就是说,您需要注入 一个代理对象,它公开与 Scoped 对象相同的公共接口,但可以 还可以从相关范围(如 HTTP 请求)中检索真正的目标对象 并将方法调用委托到真实对象上。spring-doc.cadn.net.cn

您也可以使用<aop:scoped-proxy/>在 bean 之间,这些 bean 的范围限定为singleton, 然后,引用通过可序列化的中间代理 因此能够在反序列化时重新获取目标 singleton bean。spring-doc.cadn.net.cn

声明<aop:scoped-proxy/>针对范围prototype、每种方法 调用会导致创建一个新的目标实例,该实例的 然后,呼叫将被转发。spring-doc.cadn.net.cn

此外,作用域代理并不是从较短的作用域访问 bean 的唯一方法 生命周期安全时尚。您还可以声明注入点(即 constructor 或 setter 参数或 autowired 字段)设置为ObjectFactory<MyTargetBean>, 允许getObject()调用以按需检索当前实例 时间 — 无需保留实例或单独存储实例。spring-doc.cadn.net.cn

作为扩展变体,您可以声明ObjectProvider<MyTargetBean>它提供 几种其他访问变体,包括getIfAvailablegetIfUnique.spring-doc.cadn.net.cn

它的 JSR-330 变体称为Provider,并与Provider<MyTargetBean>声明和相应的get()call 进行检索。 有关 JSR-330 的更多详细信息,请参阅此处spring-doc.cadn.net.cn

以下示例中的配置只有一行,但对于 了解其背后的 “为什么” 和 “如何”:spring-doc.cadn.net.cn

<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- an HTTP Session-scoped bean exposed as a proxy -->
	<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
		<!-- instructs the container to proxy the surrounding bean -->
		<aop:scoped-proxy/> (1)
	</bean>

	<!-- a singleton-scoped bean injected with a proxy to the above bean -->
	<bean id="userService" class="com.something.SimpleUserService">
		<!-- a reference to the proxied userPreferences bean -->
		<property name="userPreferences" ref="userPreferences"/>
	</bean>
</beans>
1 定义代理的行。

要创建这样的代理,请插入一个子级<aop:scoped-proxy/>元素转换为 作用域 Bean 定义(请参见选择要创建的代理类型和基于 XML 架构的配置)。spring-doc.cadn.net.cn

为什么 bean 的定义范围限定在request,session和 custom-scope 级别需要<aop:scoped-proxy/>元素? 考虑以下单例 bean 定义,并将其与 您需要为上述范围定义什么(请注意,以下内容userPreferencesbean 定义是不完整的):spring-doc.cadn.net.cn

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

<bean id="userManager" class="com.something.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

在前面的示例中,单例 Bean (userManager) 注入引用 到 HTTPSession-作用域的 bean (userPreferences).这里的要点是,userManagerBean 是一个单例:它每 container 及其依赖项(在本例中只有一个userPreferencesbean) 为 也只注射了一次。这意味着userManagerbean 仅在 完全相同userPreferencesobject(即最初注入它的那个)。spring-doc.cadn.net.cn

当将生存期较短的作用域 bean 注入 生存期较长的作用域 Bean(例如,注入 HTTPSession-范围的协作 bean 作为单例 bean 的依赖项)。相反,您需要一个userManager对象,并且对于 HTTP 的生命周期Session,您需要一个userPreferences对象 特定于 HTTPSession.因此,容器会创建一个对象,该对象 公开与UserPreferences类(理想情况下是 对象,它是一个UserPreferences实例),该实例可以获取真实的UserPreferences对象(HTTP 请求、Session,等等 forth)。容器将此代理对象注入到userManagerbean,即 不知道这个UserPreferencesreference 是代理。在此示例中,当UserManager实例在 Dependency-InjectedUserPreferences对象,它实际上是在代理上调用一个方法。然后,代理会获取真实的UserPreferences对象Session并委派 方法调用到检索到的 realUserPreferences对象。spring-doc.cadn.net.cn

因此,在注入时,您需要以下(正确且完整)的配置request-session-scopedbean 转换为协作对象,如下例所示 显示:spring-doc.cadn.net.cn

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
	<aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

选择要创建的代理类型

默认情况下,当 Spring 容器为标记为 这<aop:scoped-proxy/>元素中,将创建一个基于 CGLIB 的类代理。spring-doc.cadn.net.cn

CGLIB 代理不会拦截私有方法。尝试调用私有方法 在这样的代理上,它不会委托给实际的 Scoped Target 对象。spring-doc.cadn.net.cn

或者,您可以将 Spring 容器配置为创建标准 JDK 此类作用域 bean 的基于接口的代理,通过指定false对于 这proxy-target-class属性的<aop:scoped-proxy/>元素。使用 JDK 基于接口的代理意味着您的 application classpath 来影响此类代理。但是,这也意味着 作用域 Bean 必须至少实现一个接口,并且所有协作者 作用域 bean 注入到其中,必须通过其 接口。以下示例显示了基于接口的代理:spring-doc.cadn.net.cn

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
	<aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
	<property name="userPreferences" ref="userPreferences"/>
</bean>

有关选择基于类或基于接口的代理的更多详细信息, 请参见代理机制spring-doc.cadn.net.cn

直接注入请求/会话引用

作为工厂范围的替代方案,SpringWebApplicationContext还支持 注射HttpServletRequest,HttpServletResponse,HttpSession,WebRequest和 (如果存在 JSF)FacesContextExternalContext到 Spring 管理的 bean,只需通过常规注入旁边的基于类型的自动装配即可 其他Beans的分数。Spring 通常会为这样的请求和会话注入代理 对象,它具有在单例 bean 和可序列化 bean 中工作的优势 此外,类似于工厂范围的 bean 的范围代理。spring-doc.cadn.net.cn

自定义范围

Bean 范围机制是可扩展的。您可以定义自己的 范围,甚至重新定义现有范围,尽管后者被认为是不好的做法 并且您无法覆盖内置的singletonprototype范围。spring-doc.cadn.net.cn

创建自定义范围

要将自定义范围集成到 Spring 容器中,您需要实现org.springframework.beans.factory.config.Scope接口,具体如下 部分。有关如何实现自己的范围的想法,请参阅ScopeSpring Framework 本身提供的实现以及Scopejavadoc 中, 其中更详细地解释了您需要实现的方法。spring-doc.cadn.net.cn

Scopeinterface 有四种方法可以从 scope 中获取对象,从 范围,然后销毁它们。spring-doc.cadn.net.cn

例如,会话范围的实现返回会话范围的 Bean(如果它 不存在,则该方法在将 Bean 绑定到 会话以供将来参考)。以下方法从 底层范围:spring-doc.cadn.net.cn

Object get(String name, ObjectFactory<?> objectFactory)
fun get(name: String, objectFactory: ObjectFactory<*>): Any

例如,会话范围实现从 基础会话。应该返回对象,但您可以返回null如果 找不到具有指定名称的对象。以下方法从 底层范围:spring-doc.cadn.net.cn

Object remove(String name)
fun remove(name: String): Any

以下方法注册一个回调,范围应在 destroyed 或当 scope 中的指定对象被销毁时:spring-doc.cadn.net.cn

void registerDestructionCallback(String name, Runnable destructionCallback)
fun registerDestructionCallback(name: String, destructionCallback: Runnable)

有关销毁回调的更多信息,请参见 javadoc 或 Spring 范围实现。spring-doc.cadn.net.cn

以下方法获取基础范围的聊天标识符:spring-doc.cadn.net.cn

String getConversationId()
fun getConversationId(): String

此标识符对于每个范围都不同。对于会话范围的实现,此 identifier 可以是会话标识符。spring-doc.cadn.net.cn

使用自定义范围

编写和测试一个或多个自定义Scopeimplementations,您需要进行 Spring 容器知道您的新范围。以下方法是中心 方法注册新的Scope使用 Spring 容器:spring-doc.cadn.net.cn

void registerScope(String scopeName, Scope scope);
fun registerScope(scopeName: String, scope: Scope)

此方法在ConfigurableBeanFactory接口,该接口可用 通过BeanFactory大多数混凝土上的特性ApplicationContextSpring 附带的实现。spring-doc.cadn.net.cn

第一个参数registerScope(..)method 是与 一个范围。Spring 容器本身中此类名称的示例包括singletonprototype.的第二个参数registerScope(..)method 是一个实际实例 的定制Scope实现。spring-doc.cadn.net.cn

假设您编写了自定义Scopeimplementation 的 Implementation,然后按如下所示注册它 在下一个示例中。spring-doc.cadn.net.cn

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

然后,您可以创建符合自定义范围规则的 Bean 定义Scope如下:spring-doc.cadn.net.cn

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

使用自定义Scopeimplementation 中,您不仅限于编程注册 的范围。您还可以执行Scope注册,通过使用CustomScopeConfigurer类,如下例所示:spring-doc.cadn.net.cn

<?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
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://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/><bean>声明FactoryBeanimplementation 时,是工厂 Bean 本身被限定为 Scope,而不是对象 返回自getObject().