对于最新稳定版本,请使用Spring Framework 7.0.1spring-doc.cadn.net.cn

Beans瞄准镜

当你创建豆子定义时,你是在创建一个实际实例的配方 该类的 bean 定义。Beans定义是 Recipe 很重要,因为它意味着像类一样,你可以创建许多对象 来自单一配方的实例。spring-doc.cadn.net.cn

你不仅可以控制各种依赖关系和配置值,这些都涉及 可以插入一个由特定BEAN定义创建的对象,同时也控制 根据特定BEAN定义创建的对象范围。这种方法如下 强大且灵活,因为你可以选择你所创建对象的范围 通过配置,而不是在 Java 中将对象的范围烘焙进去 班级层级。豆子可以定义为部署在多个范围之一。 Spring 框架支持六个作用域,其中四个仅在 你用的是Web-Aware。应用上下文.你也可以自定义瞄准镜。spring-doc.cadn.net.cn

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

表1。豆式望远镜
范围 描述

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

(默认)将单个 Bean 定义映射到每个 Spring IoC 的单个对象实例 容器。spring-doc.cadn.net.cn

原型spring-doc.cadn.net.cn

将单个豆定义作用域为任意数量的对象实例。spring-doc.cadn.net.cn

请求spring-doc.cadn.net.cn

将单个 bean 定义作用域到单个 HTTP 请求的生命周期。那是 每个HTTP请求都有自己由单个豆子创建的豆子实例 定义。仅在具备网络功能的 Spring 环境中有效应用上下文.spring-doc.cadn.net.cn

会期spring-doc.cadn.net.cn

将单个豆定义作用域到HTTP的生命周期会期.仅在 网络感知春季的背景应用上下文.spring-doc.cadn.net.cn

应用spring-doc.cadn.net.cn

将单个豆子定义范围到一个生命周期ServletContext.仅在 网络感知春季的背景应用上下文.spring-doc.cadn.net.cn

Websocketspring-doc.cadn.net.cn

将单个豆子定义范围到一个生命周期WebSocket.仅在 网络感知春季的背景应用上下文.spring-doc.cadn.net.cn

线程作用域可用,但默认不注册。欲了解更多信息, 参见相关文档SimpleThreadScope(简易线程镜). 关于如何注册此或任何其他自定义示波器的说明,请参见“使用自定义示波器”。

单例瞄准镜

仅管理一个共享的单例豆,所有对豆子的请求 与该豆定义相符的ID会得到那个特定的豆子 实例被 Spring 容器返回。spring-doc.cadn.net.cn

换句话说,当你定义一个豆子定义并且它的作用域为 单例,Spring IoC 容器只创建一个对象实例 由那个豆子定义来定义。该单一实例存储在此类缓存中 Singleton 豆子,以及所有后续关于该命名豆子的请求和参考 返回缓存对象。下图展示了单例示波器的工作原理:spring-doc.cadn.net.cn

单身 人士

Spring 的单点豆概念不同于定义在 《四人帮(火杯)》图案书。GoF单例硬编码了 使得每个对象只创建一个特定类的一个实例 ClassLoader。Spring单例的范围最好用单个容器来描述 以及皮豆。这意味着,如果你为某一特定类定义一个豆子 单个 Spring 容器,Spring 容器只创建一个实例 该类的 bean 定义。单例示波器是默认的示波器 春季。要在XML中定义豆子为单例,可以定义豆子,如图所示 以下示例: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"/>

原型瞄准镜

豆子部署的非单例原型范围导致了新的 每当对该特定豆子的请求出现时,都会实例。也就是说,豆子 被注入到另一颗豆子中,或者你通过getBean()方法调用 容器。通常情况下,你应该对所有有状态的豆子和 无状态Beans的单例范围。spring-doc.cadn.net.cn

下图展示了Spring原型的示波器:spring-doc.cadn.net.cn

原型

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

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

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

与其他示波器不同,Spring 不管理 原型豆。容器实例化、配置并以其他方式组装一个 原型对象并将其交给客户端,不再记录该原型 实例。因此,尽管初始化生命周期回调方法在所有 上都被调用 对象无论范围大小,原型中配置销毁 生命周期回调不被调用。客户端代码必须清理原型范围 物品并释放原型豆所持有的昂贵资源。为了获得 Spring 容器释放原型范围豆子所持有的资源,尝试使用 自定义豆后处理机,包含需要清理的豆子的参考。spring-doc.cadn.net.cn

在某些方面,春季容器作为原型瞄准镜豆的作用是 Java 的替代新增功能算子。之后的所有生命周期管理都必须如此 让客户处理。(关于春季Beans生命周期的详细信息) 容器,详见生命周期回调。)spring-doc.cadn.net.cn

带有原型豆依赖的单例豆

当你使用依赖原型豆子的单例范围豆子时,请注意 依赖关系在实例化时被解析。因此,如果你对 a 进行依赖注入 原型范围豆被实例化为单例范围豆,新的原型豆被实例化 然后通过依赖注入单粒豆。原型实例是唯一的 实例 曾被提供给单例范围的豆子。spring-doc.cadn.net.cn

然而,假设你希望单例范围的豆子获取一个新的实例 在运行时反复以原型范围进行 Bean 的作。你不能对 a 原型镜豆子注入你的单粒豆子,因为这种注射只会发生 一次,当Spring容器实例化单例豆并解析时 并注入其依赖关系。如果你需要一个新的原型豆实例,请在 运行时多次,详见方法注入spring-doc.cadn.net.cn

请求、会话、应用和WebSocket作用域

请求,会期,应用Websocket仅提供示波器 如果你使用网页感知的 Spring应用上下文实现(例如:XmlWeb应用上下文).如果你用这些示波器搭配普通的Spring IoC容器, 例如:ClassPathXmlApplicationContext非法州例外抱怨的 大约是一个未知的豆子镜。spring-doc.cadn.net.cn

初始网页配置

支持Beans在请求,会期,应用Websocket层级(网页范围豆),一些次要初始配置为 在定义你的豆子之前必须这样做。(此初始设置并非必需 标准瞄准镜:单身 人士原型.)spring-doc.cadn.net.cn

如何实现初始设置取决于你具体的 Servlet 环境。spring-doc.cadn.net.cn

如果你在 Spring Web MVC 中访问了 scopeed beans,实际上是在请求中 由Spring处理调度器服务,无需特殊设置。调度器服务已经暴露了所有相关状态。spring-doc.cadn.net.cn

如果你使用 Servlet web 容器,且请求在 Spring 之外处理调度器服务(例如,使用 JSF 时),你需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener. 这可以通过使用WebApplicationInitializer接口。 或者,在你的网页应用中添加以下声明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.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>

调度器服务,RequestContextListenerRequestContextFilter所有这些都能完全正确 同样的步骤,也就是将 HTTP 请求对象绑定到线那就是维修 那个请求。这使得请求和会话范围的豆子更易获得 在通话链的下游。spring-doc.cadn.net.cn

请求范围

考虑以下 XML 的豆子定义配置:spring-doc.cadn.net.cn

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

Spring 容器创建了一个新的实例登录作通过使用登录作为每一个HTTP请求定义BEAN。也就是说,登录作BEAN 在 HTTP 请求层级被限制。你可以更改内部 实例的状态可以随你意愿创建,因为其他实例 由同一人创造登录作Beans定义:不会看到这些状态变化。 它们针对个人需求而设。当请求完成处理后, 作用域为请求的 BEAN 会被丢弃。spring-doc.cadn.net.cn

当使用注释驱动组件或 Java 配置时,@RequestScope注解 可以用来将分量分配到请求范围。以下示例展示了如何 具体方法:spring-doc.cadn.net.cn

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

会话范围

考虑以下 XML 的豆子定义配置:spring-doc.cadn.net.cn

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

Spring 容器创建了一个新的实例用户偏好通过使用用户偏好BEAN 对单个 HTTP 生命周期的定义会期.其他方面 词汇,用户偏好BEAN 实际上是 HTTP 的范围会期水平。如 使用请求范围的 BEANS,你可以更改 的实例的内部状态 你可以随意创建,知道其他HTTP也存在会期实例 使用由同一实例创建的实例用户偏好豆子定义:不要看到这些 状态变化,因为它们是针对单个HTTP 特有的会期.当 HTTP会期最终被丢弃,即对该特定HTTP的范围的豆子会期也被丢弃。spring-doc.cadn.net.cn

使用注释驱动组件或 Java 配置时,可以使用@SessionScope注释以将分量分配到会期范围。spring-doc.cadn.net.cn

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

应用范围

考虑以下 XML 的豆子定义配置:spring-doc.cadn.net.cn

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

Spring 容器创建了一个新的实例应用偏好通过使用应用偏好BEAN 定义一次,适用于整个 Web 应用。也就是说,应用偏好豆子在ServletContext并作为常规存储ServletContext属性。这与春季单粒豆有些相似,但 有两个重要区别:它是单例ServletContext,而非每春应用上下文(在任何给定的网页应用中可能有多个) 而且它实际上是暴露的,因此可以被看到为ServletContext属性。spring-doc.cadn.net.cn

使用注释驱动组件或 Java 配置时,可以使用@ApplicationScope注释以将分量分配到应用范围。这 以下示例展示了如何实现:spring-doc.cadn.net.cn

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

WebSocket 范围

WebSocket 作用域与 WebSocket 会话的生命周期相关,适用于 关于WebSocket应用的STOMP,详情请参见WebSocket范围spring-doc.cadn.net.cn

Scoped Beans 作为依赖

Spring IoC 容器不仅管理你的对象(豆子)实例化, 还有协作者(或依赖关系)的布线。如果你想注射(对于 例如)将一个HTTP请求范围的豆子连接到另一个寿命更长的子豆,你可以 选择注入AOP代理代替被放大镜的豆子。也就是说,你需要注射 一个代理对象,它暴露了与作用域对象相同的公共接口,但 还要从相关作用域(如HTTP请求)中获取真实目标对象。 并将方法调用委托到真实对象上。spring-doc.cadn.net.cn

你也可以使用<aop:scoped-proxy/>在作用域为单身 人士, 引用随后通过一个可序列化的中间代理 因此能够在反序列化时重新获得目标单例豆。spring-doc.cadn.net.cn

宣告时<aop:scoped-proxy/>面对规模的豆子原型,每一种方法 调用共享代理会导致创建一个新的目标实例,针对该实例 接着电话被转接。spring-doc.cadn.net.cn

另外,带示波器的代理并不是访问短示波器豆子的唯一方式 生命周期安全时尚。你也可以声明你的注射点(即 构造者或设定者参数或自动接线字段)作为ObjectFactory<我的目标豆>, 考虑getObject()每次调用以按需检索当前实例 需要时间——而不是保留实例或单独存储。spring-doc.cadn.net.cn

作为扩展变体,你可以声明ObjectProvider<MyTargetBean>这会带来 还有几种额外的通行变体,包括getIf可用getIfUnique(独特).spring-doc.cadn.net.cn

JSR-330的变体称为提供商并且与提供者<MyTargetBean>声明及相应的get()呼叫所有回收尝试。 关于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

为什么Beans的定义会在请求,会期以及自定义瞄准镜 关卡要求<aop:scoped-proxy/>常见场景中的元素? 考虑以下单点豆的定义,并将其与 你需要为上述作用域定义什么(注意以下内容用户偏好目前的豆子定义尚不完整):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>

在前面的例子中,单例豆(用户管理器)被注入一个参考 HTTP会期-有望远镜的豆(用户偏好).这里的重点是用户管理器豆子是单元素集:每颗豆子被恰好实例化一次 容器及其依赖关系(此例中仅有一个,即用户偏好豆子)是 而且只注射过一次。这意味着用户管理器豆子只在 完全一样用户偏好对象(即最初注入的对象)。spring-doc.cadn.net.cn

当你把短寿命的有镜豆注射到一个 更长寿命的 scoped bean(例如,注入 HTTP会期-有范围的合作 豆子作为单粒豆的依赖)。相反,你需要一个单人用户管理器对象,以及 HTTP 的生命周期内会期,你需要一个用户偏好对象 该 是 HTTP 特有的会期.因此,容器创造了一个对象 暴露了与用户偏好理想情况下,类(一个 对象是用户偏好实例),它可以获取实数用户偏好对象来自作用域机制(HTTP 请求,会期,因此 第四)。容器将该代理对象注入到用户管理器豆子,也就是 却不知道用户偏好参考是代理。在这个例子中,当用户管理器实例调用依赖注入的方法用户偏好对象,实际上是在代理上调用一个方法。代理随后取出真实值用户偏好来自(此例中)HTTP 的对象会期以及 方法调用到检索的实数用户偏好对象。spring-doc.cadn.net.cn

因此,注入时需要以下(正确且完整的)配置请求-会话范围将豆子变成协作对象,如下示例 显示: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/>element,创建一个基于CGLIB的类代理。spring-doc.cadn.net.cn

CGLIB代理不会拦截私有方法。尝试调用私有方法 在这样的代理中,代理不会委派给实际作用范围的目标对象。spring-doc.cadn.net.cn

或者,你可以配置 Spring 容器来创建标准的 JDK 基于接口的代理,用于此类有望域的豆子,具体为false对于 这代理目标类属性<aop:scoped-proxy/>元素。使用 JDK 基于接口的代理意味着你不需要在 应用类路径以影响此类代理。然而,这也意味着 的类 Scoped 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

定制瞄准镜

豆子的范围机制是可扩展的。你可以定义自己的 范围,甚至重新定义现有范围,尽管后者被认为是不良做法 而且你无法覆盖内置的单身 人士原型范围。spring-doc.cadn.net.cn

创建自定义范围

要将自定义范围集成到 Spring 容器中,你需要实现org.springframework.beans.factory.config.Scope.接口,具体描述如下 部分。关于如何实现自己的示波器,请参见范围这些实现与 Spring Framework 本身以及范围Javadoc, 这更详细地解释了你需要实施的方法。spring-doc.cadn.net.cn

范围接口有四种方法:从作用域获取对象,从中移除 瞄准镜,让他们被摧毁。spring-doc.cadn.net.cn

例如,会话范围实现会返回会话范围的豆子(如果 不存在时,方法在将豆子绑定为 会议内容,供今后参考)。以下方法从以下返回对象 基础范围:spring-doc.cadn.net.cn

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

例如,会话范围实现会从以下 底层会议。物品应该被归还,但你可以归还如果 找不到指定名称的对象。以下方法将对象从 基本范围:spring-doc.cadn.net.cn

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

以下方法注册一个回调,作用域应在 当作用波中指定的对象被销毁时,或是被摧毁: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

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

使用自定义瞄准镜

在你编写并测试一个或多个自定义软件之后范围实现,你需要做 春季容器知道你的新瞄准镜。以下方法是核心 注册新方法范围使用春季容器:spring-doc.cadn.net.cn

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

该方法声明为ConfigurableBeanFactory该接口是可用的 通过豆子工厂大部分混凝土上的财产应用上下文这些实现与 Spring 一起发布。spring-doc.cadn.net.cn

第一个论点registerScope(..)方法是与 相关的唯一名称 一个瞄准镜。Spring 容器中此类名称的例子有单身 人士原型.第二个论证registerScope(..)方法是一个实际实例 习俗范围你希望注册和使用的实现。spring-doc.cadn.net.cn

假设你写下你的定制范围实现,然后按图示注册 下一个例子。spring-doc.cadn.net.cn

下一个例子使用SimpleThreadScope(简易线程镜),该项目包含在春季中,但不包含在内 默认注册。你自己的定制说明也一样范围实现。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)

然后你可以创建符合自定义范围规则的豆子定义范围如下:spring-doc.cadn.net.cn

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

有个习俗范围实施时,你不局限于程序注册 范围的。你也可以这样做范围声明式注册,通过使用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/><豆子>声明工厂豆实现时,作用域是工厂豆本身,而不是对象 返回getObject().