?
This document uses PHP Chinese website manual Release
Spring提供了TargetSource的概念,由org.springframework.aop.TargetSource
接口進(jìn)行描述。
這個(gè)接口負(fù)責(zé)返回一個(gè)實(shí)現(xiàn)連接點(diǎn)的“目標(biāo)對(duì)象(target object)”。每當(dāng)AOP代理處理一個(gè)方法調(diào)用時(shí)都會(huì)向TargetSource
的實(shí)現(xiàn)請(qǐng)求一個(gè)目標(biāo)實(shí)例。
使用Spring AOP的開(kāi)發(fā)者通常不需要直接和TargetSource打交道,但這提供了一種強(qiáng)大的方式來(lái)支持池化(pooling),熱交換(hot swappable)和其它高級(jí)目標(biāo)。 例如,一個(gè)使用池來(lái)管理實(shí)例的TargetSource可以為每個(gè)調(diào)用返回一個(gè)不同的目標(biāo)實(shí)例。
如果你不指定一個(gè)TargetSource,一個(gè)缺省實(shí)現(xiàn)將被使用,它包裝一個(gè)本地對(duì)象。對(duì)于每次調(diào)用它將返回相同的目標(biāo)(像你期望的那樣)。
讓我們看看Spring提供的標(biāo)準(zhǔn)目標(biāo)源(target source)以及如何使用它們。
當(dāng)使用一個(gè)自定義的目標(biāo)源,你的目標(biāo)通常需要是一個(gè)原型而不是一個(gè)單例的bean定義。這允許Spring在必要時(shí)創(chuàng)建新的目標(biāo)實(shí)例。
org.springframework.aop.target.HotSwappableTargetSource
允許當(dāng)調(diào)用者保持引用的時(shí)候,切換一個(gè)AOP代理的目標(biāo)。
修改目標(biāo)源的目標(biāo)將立即生效。
HotSwappableTargetSource
是線程安全的。
你可以通過(guò)HotSwappableTargetSource的 swap()
方法來(lái)改變目標(biāo),就像下面那樣:
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); Object oldTarget = swapper.swap(newTarget);
所需的XML定義看起來(lái)像下面這樣:
<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()
調(diào)用修改了swappable bean的目標(biāo)。保持對(duì)這個(gè)bean的引用的客戶將不知道發(fā)生了這個(gè)修改,但是將可以立即點(diǎn)擊新的目標(biāo)。
這個(gè)例子沒(méi)有添加任何通知--也不必為使用一個(gè)TargetSource
添加任何通知--當(dāng)然任何TargetSource
都可以與任意通知聯(lián)合使用。
使用一個(gè)池化目標(biāo)源提供了和無(wú)狀態(tài)session EJB類(lèi)似的編程模型,它維護(hù)一個(gè)包括相同實(shí)例的池,方法調(diào)用結(jié)束后將把對(duì)象釋放回池中。
Spring池化和SLSB池化之間的一個(gè)決定性區(qū)別是Spring池化功能可以用于任何POJO。就像Spring通常情況下那樣,這個(gè)服務(wù)是非侵入式的。
Spring對(duì)Jakarta Commons Pool 1.3提供了開(kāi)箱即用的支持,后者提供了一個(gè)相當(dāng)有效的池化實(shí)現(xiàn)。要使用這個(gè)特性,你需要在應(yīng)用程序路徑中存在commons-pool的Jar文件。
也可以通過(guò)繼承org.springframework.aop.target.AbstractPoolingTargetSource
來(lái)支持其它的池化API。
下面是示例配置:
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" scope="prototype"> ... properties omitted </bean> <bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource"> <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>
注意目標(biāo)對(duì)象--例子里的“businessObjectTarget”--必須是個(gè)原型。
這允許PoolingTargetSource
的實(shí)現(xiàn)在必要時(shí)為目標(biāo)創(chuàng)建新的實(shí)例來(lái)增大池的容量。
查看AbstractPoolingTargetSource
和你想要使用的具體子類(lèi)的Javadoc獲取更多關(guān)于它屬性的信息:maxSize是最基礎(chǔ)的,而且永遠(yuǎn)都要求被提供。
在這個(gè)例子里,“myInterceptor”是一個(gè)攔截器的名字,這個(gè)攔截器需要在同一個(gè)IoC上下文中被定義。然而,定義對(duì)攔截器進(jìn)行池化是不必要的。 如果你想要的只是池化而沒(méi)有其它通知,就不要設(shè)置interceptorNames屬性。
可以配置Spring來(lái)把任何被池化對(duì)象轉(zhuǎn)型到org.springframework.aop.target.PoolingConfig
接口,
這通過(guò)一個(gè)introduction暴露配置以及當(dāng)前池的大小。你需要像這樣定義一個(gè)通知器:
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="poolTargetSource"/> <property name="targetMethod" value="getPoolingConfigMixin"/> </bean>
這個(gè)通知器可以通過(guò)調(diào)用AbstractPoolingTargetSource
類(lèi)上的一個(gè)方便的方法來(lái)獲得,因此這里使用MethodInvokingFactoryBean。
這個(gè)通知器名(這里是“poolConfigAdvisor”)必須在提供被池化對(duì)象的ProxyFactoryBean里的攔截器名列表里中。
轉(zhuǎn)型看起來(lái)像這樣:
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); System.out.println("Max pool size is " + conf.getMaxSize());
池化無(wú)狀態(tài)服務(wù)對(duì)象通常是不必要的。我們不認(rèn)為這(池化)應(yīng)當(dāng)是缺省的選擇,因?yàn)槎鄶?shù)無(wú)狀態(tài)對(duì)象是先天線程安全的,如果資源被緩存,那么對(duì)實(shí)例進(jìn)行池化會(huì)引起很多問(wèn)題。
使用自動(dòng)代理時(shí)池化更加簡(jiǎn)單。可以為任何自動(dòng)代理創(chuàng)建器設(shè)置所使用的TargetSource
建立一個(gè)“原型”目標(biāo)源和池化TargetSource很相似。在這個(gè)例子里,當(dāng)每次方法調(diào)用時(shí),將創(chuàng)建一個(gè)目標(biāo)的新實(shí)例。 雖然在新版本的JVM中創(chuàng)建一個(gè)新對(duì)象的代價(jià)并不高,但是把新對(duì)象織入(滿足它的IoC依賴)可能是很昂貴的。因此如果沒(méi)有很好的理由,你不應(yīng)該使用這個(gè)方法。
為了做到這點(diǎn),你可以把上面的poolTargetSource
定義修改成下面的形式。(為了清楚說(shuō)明,修改了bean的名字。)
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource"> <property name="targetBeanName" ref="businessObjectTarget"/> </bean>
這里只有一個(gè)屬性:目標(biāo)bean的名字。TargetSource的實(shí)現(xiàn)使用繼承來(lái)確保命名的一致性。就像池化目標(biāo)源那樣,目標(biāo)bean必須是一個(gè)原型的bean定義。
如果你需要為每個(gè)進(jìn)來(lái)的請(qǐng)求(即每個(gè)線程)創(chuàng)建一個(gè)對(duì)象,ThreadLocal
目標(biāo)源是很有用的。
ThreadLocal
的概念提供了一個(gè)JDK范圍的功能,這可以為一個(gè)線程透明的保存資源。建立一個(gè)
ThreadLocalTargetSource
的過(guò)程和其它目標(biāo)源幾乎完全一樣:
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource"> <property name="targetBeanName" value="businessObjectTarget"/> </bean>
如果不正確的在一個(gè)多線程和多類(lèi)加載器的環(huán)境里使用ThreadLocal,將帶來(lái)嚴(yán)重的問(wèn)題(可能潛在地導(dǎo)致內(nèi)存泄漏)。
永遠(yuǎn)記住應(yīng)該把一個(gè)threadlocal包裝在其它的類(lèi)里,并永遠(yuǎn)不要直接使用ThreadLocal
本身(當(dāng)然是除了threadlocal包裝類(lèi)之外)。
同時(shí),永遠(yuǎn)記住正確的設(shè)置(set)和取消(unset)(后者僅僅需要調(diào)用ThreadLocal.set(null)
)綁定到線程的本地資源。
取消在任何情況下都應(yīng)該進(jìn)行,否則也許會(huì)導(dǎo)致錯(cuò)誤的行為。Spring的ThreadLocal支持將為你處理這個(gè)問(wèn)題,所以如果沒(méi)有其它正確的處理代碼,永遠(yuǎn)應(yīng)該考慮使用這個(gè)功能。