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