使用实现TargetSource

Spring 提供了在接口中表示的 , 的概念。此接口负责 返回实现连接点的 “target object”。每次 AOP 代理处理方法时,都会请求目标实例的实现 调用。TargetSourceorg.springframework.aop.TargetSourceTargetSourcespring-doc.cn

使用 Spring AOP 的开发人员通常不需要直接使用实现,但是 这提供了一种强大的方法来支持池化、热插拔和其他 复杂的目标。例如,池化可以返回不同的目标 instance 的实例,方法是使用池来管理实例。TargetSourceTargetSourcespring-doc.cn

如果未指定 a ,则默认实现用于包装 local 对象。每次调用都会返回相同的目标(如您所料)。TargetSourcespring-doc.cn

本节的其余部分介绍了 Spring 提供的标准目标源以及如何使用它们。spring-doc.cn

使用自定义目标源时,您的目标通常需要是原型 而不是单例 bean 定义。这允许 Spring 创建一个新目标 实例。

热插拔目标源

存在让目标 的 AOP 代理,同时让调用者保留对它的引用。org.springframework.aop.target.HotSwappableTargetSourcespring-doc.cn

更改目标源的目标将立即生效。这是线程安全的。HotSwappableTargetSourcespring-doc.cn

您可以使用 HotSwappableTargetSource 上的方法来更改目标,如下例所示:swap()spring-doc.cn

HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)

以下示例显示了所需的 XML 定义:spring-doc.cn

<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>

前面的调用更改了可交换 bean 的目标。持有 引用时,该 bean 不知道更改,但立即开始点击 新目标。swap()spring-doc.cn

虽然这个例子没有添加任何 advice(没有必要在 使用 a ),any 可以与 武断的建议。TargetSourceTargetSourcespring-doc.cn

池化目标源

使用池化目标源提供与无状态会话类似的编程模型 EJB,其中维护了一个相同实例的池,具有方法调用 正在释放池中的对象。spring-doc.cn

Spring 池和 SLSB 池之间的一个关键区别是 Spring 池可以 应用于任何 POJO。与一般的 Spring 一样,此服务可以应用于 非侵入性方式。spring-doc.cn

Spring 提供了对 Commons Pool 2.2 的支持,它提供了一个 相当有效的池实现。您需要 Jar application 的 Classpath 来使用此功能。你也可以子类来支持任何其他 pooling API 的 API 中。commons-poolorg.springframework.aop.target.AbstractPoolingTargetSourcespring-doc.cn

Commons Pool 1.5+ 也受支持,但从 Spring Framework 4.2 开始已弃用。

下面的清单显示了一个示例配置:spring-doc.cn

<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>

请注意,目标对象(在前面的示例中为 )必须是 原型。这允许 implementation 创建新实例 的目标以根据需要增加池。请参阅 AbstractPoolingTargetSource 的 javadoc 和您希望使用的具体子类以获取信息 关于其属性。 是最基本的,并且始终保证存在。businessObjectTargetPoolingTargetSourcemaxSizespring-doc.cn

在本例中, 是需要 在相同的 IoC 上下文中定义。但是,您无需指定拦截器来 使用池化。如果只需要 pooling 而不想要其他建议,则根本不要设置该属性。myInterceptorinterceptorNamesspring-doc.cn

你可以配置 Spring 以便能够将任何池化对象强制转换为接口,从而公开信息 通过简介介绍池的配置和当前大小。你 需要定义一个类似于以下内容的 advisor:org.springframework.aop.target.PoolingConfigspring-doc.cn

<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
	<property name="targetObject" ref="poolTargetSource"/>
	<property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>

此顾问是通过对类调用便捷方法获得的,因此使用 .这 advisor 的名称 (, here) 必须在 公开 pooled 对象的 API。AbstractPoolingTargetSourceMethodInvokingFactoryBeanpoolConfigAdvisorProxyFactoryBeanspring-doc.cn

演员定义如下:spring-doc.cn

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)
通常不需要池化无状态服务对象。我们认为不应该 是默认选项,因为大多数无状态对象本质上是线程安全的,而实例 如果缓存了资源,则池化是有问题的。

使用自动代理可以实现更简单的池化。您可以设置实现 由任何 Auto-Proxy Creator 使用。TargetSourcespring-doc.cn

原型目标源

设置“原型”目标源与设置池类似 。在这个 case,则在每次方法调用时都会创建一个 Target 的新实例。虽然 在现代 JVM 中,创建新对象的成本并不高,将 新对象(满足其 IoC 依赖项)可能更昂贵。因此,您不应该 使用这种方法没有很好的理由。TargetSourcespring-doc.cn

为此,您可以修改前面显示的定义,如下所示 (为清楚起见,我们还更改了名称):poolTargetSourcespring-doc.cn

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
	<property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

唯一的属性是目标 Bean 的名称。在实现中使用继承来确保命名一致。与池化目标一样 source,则目标 Bean 必须是原型 Bean 定义。TargetSourcespring-doc.cn

ThreadLocal目标源

ThreadLocal如果需要为每个源创建一个对象,则 target 源非常有用 传入请求(即每个线程)。a 的概念提供了 JDK 范围的 工具以透明方式将资源与线程一起存储。设置 a 与为其他类型的解释几乎相同 的目标源,如下例所示:ThreadLocalThreadLocalTargetSourcespring-doc.cn

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
	<property name="targetBeanName" value="businessObjectTarget"/>
</bean>
ThreadLocal实例存在严重问题(可能导致内存泄漏),当 在多线程和多类加载器环境中错误地使用它们。你 应该始终考虑将 a 包装在其他类中,并且永远不要直接使用 本身(包装器类除外)。此外,您应该 请始终记住正确设置和取消设置(如果后者涉及对 )线程本地资源的调用。取消设置应在 无论如何,因为不取消设置它可能会导致有问题的行为。Spring 的支持为您完成了此操作,并且应始终考虑使用实例,而无需其他适当的处理代码。ThreadLocalThreadLocalThreadLocal.remove()ThreadLocalThreadLocal