Spring 提供了 的概念,在界面中表示。此接口负责 返回实现连接点的“目标对象”。每次 AOP 代理处理方法时,都会要求为目标实例提供实现 调用。TargetSourceorg.springframework.aop.TargetSourceTargetSource

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

如果未指定 ,则使用默认实现来包装 本地对象。每次调用都会返回相同的目标(如您所料)。TargetSource

本节的其余部分描述了 Spring 提供的标准目标源以及如何使用它们。

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

热插拔目标源

存在让目标 切换 AOP 代理,同时让调用方保留对它的引用。org.springframework.aop.target.HotSwappableTargetSource

更改目标源的目标将立即生效。是线程安全的。HotSwappableTargetSource

可以使用 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>

前面的调用更改了可交换 Bean 的目标。持有 引用该 Bean 不知道更改,但立即开始命中 新目标。swap()

虽然这个例子没有添加任何建议(没有必要添加建议 使用 a),any 可以与 武断的建议。TargetSourceTargetSource

汇集目标源

使用池化目标源提供与无状态会话类似的编程模型 EJB,其中维护一个相同实例的池,并调用方法 转到池中的释放对象。

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

Spring 提供了对 Commons Pool 2.2 的支持,它提供了一个 相当有效的池化实现。你需要罐子在你的 应用程序的类路径来使用此功能。您还可以子类来支持任何其他类 池化 API。commons-poolorg.springframework.aop.target.AbstractPoolingTargetSource

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>

请注意,目标对象(在前面的示例中)必须是 原型。这允许实现创建新实例 的目标,以根据需要扩大池。请参阅 AbstractPoolingTargetSource 的 javadoc 和要使用的具体子类以获取信息 关于它的属性。 是最基本的,并且始终保证存在。businessObjectTargetPoolingTargetSourcemaxSize

在本例中,是需要 在相同的 IoC 上下文中定义。但是,您不需要指定拦截器 使用池化。如果您只想池化而没有其他建议,请不要设置属性。myInterceptorinterceptorNames

您可以将 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>

该顾问是通过在类上调用便利方法获得的,因此使用 .这 顾问的姓名 (, 此处) 必须位于 的 ,公开池化对象。AbstractPoolingTargetSourceMethodInvokingFactoryBeanpoolConfigAdvisorProxyFactoryBean

演员定义如下:

  • 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

原型目标源

设置“原型”目标源类似于设置池化。在这个 案例中,每次方法调用时都会创建一个目标的新实例。虽然 在现代 JVM 中,创建新对象的成本并不高,连接 新对象(满足其 IoC 依赖项)可能更昂贵。因此,您不应该 在没有充分理由的情况下使用这种方法。TargetSource

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

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

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

ThreadLocal目标来源

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

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