此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
用TargetSource
实现
Spring 提供了TargetSource
,以org.springframework.aop.TargetSource
接口。该接口负责
返回实现连接点的“目标对象”。这TargetSource
每次 AOP 代理处理方法时都会要求实现目标实例
调用。
使用 Spring AOP 的开发人员通常不需要直接使用TargetSource
实现,但
这提供了一种支持池化、热插拔和其他
复杂的目标。例如,池化TargetSource
可以返回不同的目标
实例,通过使用池来管理实例。
如果未指定TargetSource
,则默认实现用于包装
local 对象。每次调用都会返回相同的目标(正如您所期望的那样)。
本节的其余部分介绍了 Spring 提供的标准目标源以及如何使用它们。
使用自定义目标源时,您的目标通常需要是原型 而不是单例 bean 定义。这允许 Spring 创建一个新的目标 实例。 |
热插拔目标源
这org.springframework.aop.target.HotSwappableTargetSource
存在是为了让目标
的 AOP 代理进行切换,同时让调用方保留对它的引用。
更改目标源的目标立即生效。这HotSwappableTargetSource
是线程安全的。
您可以使用swap()
方法,如以下示例所示:
-
Java
-
Kotlin
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)
以下示例显示了所需的 XML 定义:
<bean id="initialTarget" class="mycompany.OldTarget"/>
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="initialTarget"/>
</bean>
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="swapper"/>
</bean>
前面的swap()
调用更改可交换 bean 的目标。持有
引用该 bean 时不知道更改,但立即开始点击
新目标。
虽然这个例子没有添加任何建议(没有必要向
使用TargetSource
)、任何TargetSource
可与
任意建议。
池化目标源
使用池化目标源提供与无状态会话类似的编程模型 EJB,其中维护相同实例的池,并调用方法。 去释放池中的对象。
Spring 池化和 SLSB 池化之间的一个关键区别在于,Spring 池化可以 应用于任何 POJO。与一般的 Spring 一样,此服务可以应用于 非侵入性方式。
Spring 为 Commons Pool 2.2 提供支持,它提供了一个
相当有效的池化实现。您需要commons-pool
Jar 在您的
应用程序的类路径以使用此功能。您还可以将子类化org.springframework.aop.target.AbstractPoolingTargetSource
支持任何其他
池化 API。
Commons Pool 1.5+ 也受支持,但从 Spring Framework 4.2 开始被弃用。 |
以下列表显示了示例配置:
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
scope="prototype">
... properties omitted
</bean>
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
<property name="maxSize" value="25"/>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="poolTargetSource"/>
<property name="interceptorNames" value="myInterceptor"/>
</bean>
请注意,目标对象 (businessObjectTarget
在前面的示例中)必须是
原型。这允许PoolingTargetSource
实现创建新实例
目标的,以根据需要扩大池。请参阅javadoc 的AbstractPoolingTargetSource
以及您希望用于信息的具体子类
关于它的属性。maxSize
是最基本的,总是保证在场。
在这种情况下,myInterceptor
是需要
在同一 IoC 上下文中定义。但是,您无需指定拦截器
使用池化。如果您只想要池化而不需要其他建议,请不要将interceptorNames
财产。
您可以将 Spring 配置为能够将任何池化对象转换为org.springframework.aop.target.PoolingConfig
接口,用于公开信息
通过介绍池的配置和当前规模。你
需要定义类似于以下内容的顾问:
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="poolTargetSource"/>
<property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>
此顾问是通过调用AbstractPoolingTargetSource
类,因此使用MethodInvokingFactoryBean
.这
顾问姓名 (poolConfigAdvisor
,此处)必须位于
这ProxyFactoryBean
,这将公开池化对象。
演员阵容定义如下:
-
Java
-
Kotlin
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize)
通常不需要池化无状态服务对象。我们认为它不应该 是默认选择,因为大多数无状态对象自然是线程安全的,并且实例 如果缓存了资源,则池化是有问题的。 |
使用自动代理可以实现更简单的池化。您可以将TargetSource
实现
由任何自动代理创建者使用。
原型目标源
设置“原型”目标源类似于设置池TargetSource
.在这个
情况下,每次方法调用时都会创建一个目标的新实例。虽然
在现代 JVM 中创建新对象的成本并不高,连接
new 对象(满足其 IoC 依赖项)可能更昂贵。因此,您不应该
没有充分理由使用这种方法。
为此,您可以修改poolTargetSource
定义如下
(为了清楚起见,我们还更改了名称):
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName" ref="businessObjectTarget"/>
</bean>
唯一的属性是目标 Bean 的名称。继承用于TargetSource
实现以确保命名的一致性。与池化目标一样
source,目标 Bean 必须是原型 Bean 定义。
ThreadLocal
目标来源
ThreadLocal
如果需要为每个对象创建对象,则目标源非常有用
传入请求(即每个线程)。的概念ThreadLocal
提供 JDK 范围的
将资源透明地存储在线程旁边。设置ThreadLocalTargetSource
与其他类型的解释几乎相同
目标源,如以下示例所示:
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
</bean>
ThreadLocal 实例会出现严重问题(可能导致内存泄漏)
在多线程和多类加载器环境中错误地使用它们。你
应始终考虑将ThreadLocal 在其他类中,并且从不直接使用
这ThreadLocal 本身(包装器类除外)。此外,你应该
始终记住正确设置和取消设置(其中后者涉及对ThreadLocal.remove() ) 线程本地资源。取消设置应在
无论如何,因为不取消设置可能会导致有问题的行为。Spring的ThreadLocal 支持会为您执行此作,并且应始终考虑使用ThreadLocal 没有其他正确处理代码的实例。 |