对于最新的稳定版本,请使用 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
支持任何其他pooling 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.set(null) ) 线程本地资源。取消设置应该在任何情况下,因为不取消设置可能会导致有问题的行为。Spring 的ThreadLocal 支持会为您执行此作,并且应始终考虑使用ThreadLocal 没有其他正确处理代码的实例。 |