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