|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
Beans瞄准镜
当你创建豆子定义时,你是在创建一个实际实例的配方 该类的 bean 定义。Beans定义是 Recipe 很重要,因为它意味着像类一样,你可以创建许多对象 来自单一配方的实例。
你不仅可以控制各种依赖关系和配置值,这些都涉及
可以插入一个由特定BEAN定义创建的对象,同时也控制
根据特定BEAN定义创建的对象范围。这种方法如下
强大且灵活,因为你可以选择你所创建对象的范围
通过配置,而不是在 Java 中将对象的范围烘焙进去
班级层级。豆子可以定义为部署在多个范围之一。
Spring 框架支持六个作用域,其中四个仅在
你用的是Web-Aware。应用上下文.你也可以自定义瞄准镜。
下表描述了支持的范围:
| 范围 | 描述 |
|---|---|
(默认)将单个 Bean 定义映射到每个 Spring IoC 的单个对象实例 容器。 |
|
将单个豆定义作用域为任意数量的对象实例。 |
|
将单个 bean 定义作用域到单个 HTTP 请求的生命周期。那是
每个HTTP请求都有自己由单个豆子创建的豆子实例
定义。仅在具备网络功能的 Spring 环境中有效 |
|
将单个豆定义作用域到HTTP的生命周期 |
|
将单个豆子定义范围到一个生命周期 |
|
将单个豆子定义范围到一个生命周期 |
线程作用域可用,但默认不注册。欲了解更多信息,
参见相关文档SimpleThreadScope(简易线程镜).
关于如何注册此或任何其他自定义示波器的说明,请参见“使用自定义示波器”。 |
单例瞄准镜
仅管理一个共享的单例豆,所有对豆子的请求 与该豆定义相符的ID会得到那个特定的豆子 实例被 Spring 容器返回。
换句话说,当你定义一个豆子定义并且它的作用域为 单例,Spring IoC 容器只创建一个对象实例 由那个豆子定义来定义。该单一实例存储在此类缓存中 Singleton 豆子,以及所有后续关于该命名豆子的请求和参考 返回缓存对象。下图展示了单例示波器的工作原理:
Spring 的单点豆概念不同于定义在 《四人帮(火杯)》图案书。GoF单例硬编码了 使得每个对象只创建一个特定类的一个实例 ClassLoader。Spring单例的范围最好用单个容器来描述 以及皮豆。这意味着,如果你为某一特定类定义一个豆子 单个 Spring 容器,Spring 容器只创建一个实例 该类的 bean 定义。单例示波器是默认的示波器 春季。要在XML中定义豆子为单例,可以定义豆子,如图所示 以下示例:
<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原型的示波器:
(数据访问对象 (DAO)通常不被配置为原型,因为典型的DAO不成立 任何对话状态。我们更容易重复利用 单元素图。)
以下示例将豆子定义为XML中的原型:
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
与其他示波器不同,Spring 不管理 原型豆。容器实例化、配置并以其他方式组装一个 原型对象并将其交给客户端,不再记录该原型 实例。因此,尽管初始化生命周期回调方法在所有 上都被调用 对象无论范围大小,原型中配置销毁 生命周期回调不被调用。客户端代码必须清理原型范围 物品并释放原型豆所持有的昂贵资源。为了获得 Spring 容器释放原型范围豆子所持有的资源,尝试使用 自定义豆后处理机,包含需要清理的豆子的参考。
在某些方面,春季容器作为原型瞄准镜豆的作用是
Java 的替代新增功能算子。之后的所有生命周期管理都必须如此
让客户处理。(关于春季Beans生命周期的详细信息)
容器,详见生命周期回调。)
带有原型豆依赖的单例豆
当你使用依赖原型豆子的单例范围豆子时,请注意 依赖关系在实例化时被解析。因此,如果你对 a 进行依赖注入 原型范围豆被实例化为单例范围豆,新的原型豆被实例化 然后通过依赖注入单粒豆。原型实例是唯一的 实例 曾被提供给单例范围的豆子。
然而,假设你希望单例范围的豆子获取一个新的实例 在运行时反复以原型范围进行 Bean 的作。你不能对 a 原型镜豆子注入你的单粒豆子,因为这种注射只会发生 一次,当Spring容器实例化单例豆并解析时 并注入其依赖关系。如果你需要一个新的原型豆实例,请在 运行时多次,详见方法注入。
请求、会话、应用和WebSocket作用域
这请求,会期,应用和Websocket仅提供示波器
如果你使用网页感知的 Spring应用上下文实现(例如:XmlWeb应用上下文).如果你用这些示波器搭配普通的Spring IoC容器,
例如:ClassPathXmlApplicationContext一非法州例外抱怨的
大约是一个未知的豆子镜。
初始网页配置
支持Beans在请求,会期,应用和Websocket层级(网页范围豆),一些次要初始配置为
在定义你的豆子之前必须这样做。(此初始设置并非必需
标准瞄准镜:单身 人士和原型.)
如何实现初始设置取决于你具体的 Servlet 环境。
如果你在 Spring Web MVC 中访问了 scopeed beans,实际上是在请求中
由Spring处理调度器服务,无需特殊设置。调度器服务已经暴露了所有相关状态。
如果你使用 Servlet web 容器,且请求在 Spring 之外处理调度器服务(例如,使用 JSF 时),你需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener.
这可以通过使用WebApplicationInitializer接口。
或者,在你的网页应用中添加以下声明web.xml文件:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
或者,如果你的听众设置有问题,可以考虑用Spring的RequestContextFilter.Filter映射依赖于周围的网络
所以你必须根据需要进行更改。以下列表
显示网页应用中的过滤部分:
<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>
调度器服务,RequestContextListener和RequestContextFilter所有这些都能完全正确
同样的步骤,也就是将 HTTP 请求对象绑定到线那就是维修
那个请求。这使得请求和会话范围的豆子更易获得
在通话链的下游。
请求范围
考虑以下 XML 的豆子定义配置:
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
Spring 容器创建了一个新的实例登录作通过使用登录作为每一个HTTP请求定义BEAN。也就是说,登录作BEAN 在 HTTP 请求层级被限制。你可以更改内部
实例的状态可以随你意愿创建,因为其他实例
由同一人创造登录作Beans定义:不会看到这些状态变化。
它们针对个人需求而设。当请求完成处理后,
作用域为请求的 BEAN 会被丢弃。
当使用注释驱动组件或 Java 配置时,@RequestScope注解
可以用来将分量分配到请求范围。以下示例展示了如何
具体方法:
-
Java
-
Kotlin
@RequestScope
@Component
public class LoginAction {
// ...
}
@RequestScope
@Component
class LoginAction {
// ...
}
会话范围
考虑以下 XML 的豆子定义配置:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
Spring 容器创建了一个新的实例用户偏好通过使用用户偏好BEAN 对单个 HTTP 生命周期的定义会期.其他方面
词汇,用户偏好BEAN 实际上是 HTTP 的范围会期水平。如
使用请求范围的 BEANS,你可以更改 的实例的内部状态
你可以随意创建,知道其他HTTP也存在会期实例
使用由同一实例创建的实例用户偏好豆子定义:不要看到这些
状态变化,因为它们是针对单个HTTP 特有的会期.当
HTTP会期最终被丢弃,即对该特定HTTP的范围的豆子会期也被丢弃。
使用注释驱动组件或 Java 配置时,可以使用@SessionScope注释以将分量分配到会期范围。
-
Java
-
Kotlin
@SessionScope
@Component
public class UserPreferences {
// ...
}
@SessionScope
@Component
class UserPreferences {
// ...
}
应用范围
考虑以下 XML 的豆子定义配置:
<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
Spring 容器创建了一个新的实例应用偏好通过使用应用偏好BEAN 定义一次,适用于整个 Web 应用。也就是说,应用偏好豆子在ServletContext并作为常规存储ServletContext属性。这与春季单粒豆有些相似,但
有两个重要区别:它是单例ServletContext,而非每春应用上下文(在任何给定的网页应用中可能有多个)
而且它实际上是暴露的,因此可以被看到为ServletContext属性。
使用注释驱动组件或 Java 配置时,可以使用@ApplicationScope注释以将分量分配到应用范围。这
以下示例展示了如何实现:
-
Java
-
Kotlin
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
@ApplicationScope
@Component
class AppPreferences {
// ...
}
WebSocket 范围
WebSocket 作用域与 WebSocket 会话的生命周期相关,适用于 关于WebSocket应用的STOMP,详情请参见WebSocket范围。
Scoped Beans 作为依赖
Spring IoC 容器不仅管理你的对象(豆子)实例化, 还有协作者(或依赖关系)的布线。如果你想注射(对于 例如)将一个HTTP请求范围的豆子连接到另一个寿命更长的子豆,你可以 选择注入AOP代理代替被放大镜的豆子。也就是说,你需要注射 一个代理对象,它暴露了与作用域对象相同的公共接口,但 还要从相关作用域(如HTTP请求)中获取真实目标对象。 并将方法调用委托到真实对象上。
|
你也可以使用 宣告时 另外,带示波器的代理并不是访问短示波器豆子的唯一方式
生命周期安全时尚。你也可以声明你的注射点(即
构造者或设定者参数或自动接线字段)作为 作为扩展变体,你可以声明 JSR-330的变体称为 |
以下示例中的配置只有一条线,但重要的是 理解背后的“为什么”以及“如何”:
<?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 模式的配置)。
为什么Beans的定义会在请求,会期以及自定义瞄准镜
关卡要求<aop:scoped-proxy/>常见场景中的元素?
考虑以下单点豆的定义,并将其与
你需要为上述作用域定义什么(注意以下内容用户偏好目前的豆子定义尚不完整):
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
在前面的例子中,单例豆(用户管理器)被注入一个参考
HTTP会期-有望远镜的豆(用户偏好).这里的重点是用户管理器豆子是单元素集:每颗豆子被恰好实例化一次
容器及其依赖关系(此例中仅有一个,即用户偏好豆子)是
而且只注射过一次。这意味着用户管理器豆子只在
完全一样用户偏好对象(即最初注入的对象)。
当你把短寿命的有镜豆注射到一个
更长寿命的 scoped bean(例如,注入 HTTP会期-有范围的合作
豆子作为单粒豆的依赖)。相反,你需要一个单人用户管理器对象,以及 HTTP 的生命周期内会期,你需要一个用户偏好对象
该 是 HTTP 特有的会期.因此,容器创造了一个对象
暴露了与用户偏好理想情况下,类(一个
对象是用户偏好实例),它可以获取实数用户偏好对象来自作用域机制(HTTP 请求,会期,因此
第四)。容器将该代理对象注入到用户管理器豆子,也就是
却不知道用户偏好参考是代理。在这个例子中,当用户管理器实例调用依赖注入的方法用户偏好对象,实际上是在代理上调用一个方法。代理随后取出真实值用户偏好来自(此例中)HTTP 的对象会期以及
方法调用到检索的实数用户偏好对象。
因此,注入时需要以下(正确且完整的)配置请求-和会话范围将豆子变成协作对象,如下示例
显示:
<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的类代理。
|
CGLIB代理不会拦截私有方法。尝试调用私有方法 在这样的代理中,代理不会委派给实际作用范围的目标对象。 |
或者,你可以配置 Spring 容器来创建标准的 JDK
基于接口的代理,用于此类有望域的豆子,具体为false对于
这代理目标类属性<aop:scoped-proxy/>元素。使用 JDK
基于接口的代理意味着你不需要在
应用类路径以影响此类代理。然而,这也意味着 的类
Scoped Bean必须至少实现一个接口,且所有协作者
注入望远镜豆的对象必须通过其中一个
接口。以下示例展示了基于接口的代理:
<!-- 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 容器中,你需要实现org.springframework.beans.factory.config.Scope.接口,具体描述如下
部分。关于如何实现自己的示波器,请参见范围这些实现与 Spring Framework 本身以及范围Javadoc,
这更详细地解释了你需要实施的方法。
这范围接口有四种方法:从作用域获取对象,从中移除
瞄准镜,让他们被摧毁。
例如,会话范围实现会返回会话范围的豆子(如果 不存在时,方法在将豆子绑定为 会议内容,供今后参考)。以下方法从以下返回对象 基础范围:
-
Java
-
Kotlin
Object get(String name, ObjectFactory<?> objectFactory)
fun get(name: String, objectFactory: ObjectFactory<*>): Any
例如,会话范围实现会从以下
底层会议。物品应该被归还,但你可以归还零如果
找不到指定名称的对象。以下方法将对象从
基本范围:
-
Java
-
Kotlin
Object remove(String name)
fun remove(name: String): Any
以下方法注册一个回调,作用域应在 当作用波中指定的对象被销毁时,或是被摧毁:
-
Java
-
Kotlin
void registerDestructionCallback(String name, Runnable destructionCallback)
fun registerDestructionCallback(name: String, destructionCallback: Runnable)
有关销毁回调的更多信息,请参阅 javadoc 或 Spring 范围实现。
以下方法获取底层范围的会话标识符:
-
Java
-
Kotlin
String getConversationId()
fun getConversationId(): String
每个范围的标识符都不同。对于会话范围的实现,这是 标识符可以是会话标识符。
使用自定义瞄准镜
在你编写并测试一个或多个自定义软件之后范围实现,你需要做
春季容器知道你的新瞄准镜。以下方法是核心
注册新方法范围使用春季容器:
-
Java
-
Kotlin
void registerScope(String scopeName, Scope scope);
fun registerScope(scopeName: String, scope: Scope)
该方法声明为ConfigurableBeanFactory该接口是可用的
通过豆子工厂大部分混凝土上的财产应用上下文这些实现与 Spring 一起发布。
第一个论点registerScope(..)方法是与 相关的唯一名称
一个瞄准镜。Spring 容器中此类名称的例子有单身 人士和原型.第二个论证registerScope(..)方法是一个实际实例
习俗范围你希望注册和使用的实现。
假设你写下你的定制范围实现,然后按图示注册
下一个例子。
下一个例子使用SimpleThreadScope(简易线程镜),该项目包含在春季中,但不包含在内
默认注册。你自己的定制说明也一样范围实现。 |
-
Java
-
Kotlin
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)
然后你可以创建符合自定义范围规则的豆子定义范围如下:
<bean id="..." class="..." scope="thread">
有个习俗范围实施时,你不局限于程序注册
范围的。你也可以这样做范围声明式注册,通过使用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
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(). |