本文翻譯自Spring.NET官方文檔Version 1.3.2。git
受限於我的知識水平,有些地方翻譯可能不許確,可是我仍是但願個人這些微薄的努力能爲他人提供幫助。spring
侵刪。數組
若是你正在爲你的業務模型使用IoC容器——這是個好主意——你將會想使用某個 Spring.NET's AOP特定的IFactoryObject 的實現(要記住,一個工廠的實例提供了一個間接層,使這個工廠可以建立不一樣類型的對象—5.3.9節,「經過使用其餘類型和實例建立一個對象」)。app
一個基本的建立Spring.NET's AOP代理的方式是使用Spring.Aop.Framework.ProxyFactoryObject類。這種作法可以讓你能靈活配置切入點的順序,切入點的應用和將會應用到你的業務中的通知。然而,若是你不須要這種這種配置,有其餘的更簡便的方法生成代理。框架
和其餘的Spring.NET IFactoryObject 的實現同樣,ProxyFactoryObject引入了一箇中間層。若是你定義了一個ProxyFactoryObject實例,名字叫作foo,那麼對foo並非ProxyFactoryObject 這個代理對象的引用,而是ProxyFactoryObject這個實例的的GetObject() 方法產生的對象。這個方法會包裝目標對象而後產生一個AOP代理。ide
使用ProxyFactoryObject 或者其餘的IoC感知類建立AOP代理的最大好處就是咱們可使用IoC來管理通知和切入點。 這個是一個強大的特性,能夠完成其餘AOP框架不能實現的一些功能。例如,一個通知可能關聯到一個應用程序中全部實體(不只僅是AOP框架中的目標實體),這個得益於依賴注入帶來的可拔插性。post
和Spring.NET中大多的IFactoryObject實現同樣,ProxyFactoryObject 也是一個能夠靈活配置的Spring.NET實體。它的配置主要用來:性能
一些關鍵的屬性繼承自Spring.Aop.Framework.ProxyConfig 類:這個類是全部Spring.NET中AOP代理工廠的父類。一些關鍵的配置包括:測試
其餘在ProxyFactoryObject 類中定義的屬性包括:優化
這些名稱都是存在於當前容器中的對象名稱,同時也包括容器的全部父類包含的對象。你不能夠直接聲明容器外的源對象,由於這麼作會致使ProxyFactoryObject 忽略通知的單例設置。
讓咱們看一個簡單的ProxyFactoryObject 的例子,這個例子包括:
1 <object id="personTarget" type="MyCompany.MyApp.Person, MyCompany">
2 <property name="name" value="Tony"/>
3 <property name="age" value="51"/>
4 </object>
5 <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
6 <property name="customProperty" value="configuration string"/>
7 </object>
8 <object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
9 </object>
10 <object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
11
12 <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
13 <property name="target" ref="personTarget"/>
14 <property name="interceptorNames">
15 <list>
16 <value>debugInterceptor</value>
17 <value>myCustomInterceptor</value>
18 </list>
19 </property>
20 </object>
要注意的是,InterceptorNames 屬性是一個字符串列表:位於當前上下文的切面名稱。通知器,攔截器,前置通知,返回後通知和拋出通知實體都能被應用進去。它們的順序是很重要的。
你可能會奇怪爲何這個列表不是實體的引用。其緣由是若是ProxyFactoryObject的singleton屬性設置成false,它必須可以針對每一個被代理的實體都返回獨立的代理實例。若是有任何的通知器是一個原型,一個獨立的實例須要被返回,因此沒有必要再從上下文獲取一個原型的實例,由於僅僅使用同一個引用是明顯不夠的。
上文中「person」對象的定義能夠用一個Iperson接口的實現來代替,以下:
IPerson person = (IPerson) factory.GetObject("person");
其餘在同一個IoC上下文的實體能夠表達一個強類型的依賴注入,和其餘普通的.NET實體同樣:
1 <object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany">
2 <property name="person" ref="person"/>
3 </object>
這裏例子中的PersonUser 類會暴露IPerson接口的一些屬性。AOP代理會被用來代替「真正的」person的實現。於是這個類型會是一個代理類型。它能夠轉換成一個IAdvised接口的實現(下文會討論)。
可使用內聯對象(inline object)來把目標和目標的代理的區別隱藏起來,就像下面這樣。(更多關於內聯對象的信息可查看5.3.2.3小節, 「Inner objects「) 只有ProxyFactoryObject 的定義是不同的;通知也包含在內,僅僅爲了完整性須要:
1 <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
2 <property name="customProperty" value="configuration string"/>
3 </object>
4 <object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
5 </object>
6 <object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
7
8 <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
9 <property name="target">
10 <!-- Instead of using a reference to target, just use an inline object -->
11 <object type="MyCompany.MyApp.Person, MyCompany">
12 <property name="name" value="Tony"/>
13 <property name="age" value="51"/>
14 </object>
15 </property>
16 <property name="interceptorNames">
17 <list>
18 <value>debugInterceptor</value>
19 <value>myCustomInterceptor</value>
20 </list>
21 </property>
22 </object>
這樣作的一個優勢是僅僅存在一個Person類型的對象:當咱們想防止應用程序上下文的使用者得到一個沒有被通知的對象的引用,或者想避免Spring IoC 的自動裝配形成的模棱兩可的定義的時候是很用用的。還有一個有爭議的優勢是若是這麼作,那麼ProxyFactoryObject 的定義是獨立完整的。然而有些時候可以從工廠中得到沒有被通知的目標對象也許也是一個好處:例如在一些特定的測試環境中。
讓咱們來看一個經過ProxyFactoryObject配置代理對象的例子。
1 <!-- create the object to reference -->
2 <object id="RealObjectTarget" type="MyRealObject" singleton="false"/>
3 <!-- create the proxied object for everyone to use-->
4 <object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
5 <property name="proxyInterfaces" value="MyInterface" />
6 <property name="isSingleton" value="false"/>
7 <property name="targetName" value="RealObjectTarget" />
8 </object>
9
若是你使用一個原型來做爲目標你必須把TargetName屬性設置成你對象的名字/id而不能使用一個目標引用對象的屬性。這樣作接下來可以經過一個新的目標原型實例生成一個對應的新代理。
考慮上面的Spring.Net配置的時候,要注意ProxyFactoryObject 的IsSingleton 屬性是設置成false的。這就意味着每個代理對象都是獨一無二的。這樣你就可使用下面的方式去配置配置每個代理對象的通知類型:
1 // Will return un-advised instance of proxy object
2 MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject"); 3 // myProxyObject1 instance now has an advice attached to it.
4 IAdvised advised = (IAdvised)myProxyObject1; 5 advised.AddAdvice( new DebugAdvice() ); 6 // Will return a new, un-advised instance of proxy object
7 MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject");
當你想代理一個類,而不是一個或者多個接口的時候,應該怎麼辦呢?
想象一下上面的例子,若是不存在IPerson 接口,咱們要通知一個沒有實現任何接口的Person類。在這種狀況下,若是找不到任何實現的接口的話,ProxyFactoryObject 會代理全部的公共的虛方法和虛屬性。咱們能夠經過設置ProxyTargetType爲true來強制Spring.NET 去代理類而不是接口。
類的代理是經過在運行時產生一個目標的子類來實現的。Spring.NET 配置這個子類來委託對原目標對象方法的調用:這個子類被用來實現這種裝飾器模式,將通知織入。
類的代理應該對使用者透明。然而須要考慮一個重要問題:非虛的方法不能被通知,就像它們不能被重寫同樣。這個可能在一些一般不會把方法聲明成虛方法的類裏面有些限制。
尤爲是在定義一個事務代理的時候,若是你沒有使用事務名稱空間,你最終可能會碰到不少相似的代理定義。使用父子對象定義,再加上一些內聯對象定義,可使得代理定義更加精簡。
首先,須要爲這個代理定義一個父對象模板:
1 <object id="txProxyTemplate" abstract="true"
2 type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
3 <property name="PlatformTransactionManager" ref="adoTransactionManager"/>
4 <property name="TransactionAttributes">
5 <name-values>
6 <add key="*" value="PROPAGATION_REQUIRED"/>
7 </name-values>
8 </property>
9 </object>
這樣它沒法實例化,因此實際上配置還還沒有完成。接下來每一個須要被建立的代理都是一個子對象定義,這個定義把目標代理包裝成一個內聯對象,由於目標對象永遠不會被本身使用:
1 <object name="testObjectManager" parent="txProxyTemplate">
2 <property name="Target">
3 <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
4 <property name="TestObjectDao" ref="testObjectDao"/>
5 </object>
6 </property>
7 </object>
固然也能夠在子對象中重寫父類模板的屬性,就像下面的事務傳播配置(transaction propagation settings)案例:
1 <object name="testObjectManager" parent="txProxyTemplate">
2 <property name="Target">
3 <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
4 <property name="TestObjectDao" ref="testObjectDao"/>
5 </object>
6 </property>
7 <property name="TransactionAttributes">
8 <name-values>
9 <add key="Save*" value="PROPAGATION_REQUIRED"/>
10 <add key="Delete*" value="PROPAGATION_REQUIRED"/>
11 <add key="Find*" value="PROPAGATION_REQUIRED,readonly"/>
12 </name-values>
13 </property>
14 </object>
要注意,上面的例子咱們已經顯示地把父類對象定義成抽象,因此它其實是不能被實例化的。應用程序上下文(並非簡單的對象工廠)會默認預實例化(pre-instantiate )全部的單例。所以,要注意的是(至少對於單例對象),若是你有一個你想做爲一個模板的父對象,而且這個模板定義了一個特定的類,你必須保證將abstract 屬性設置成true,否則應用程序上下文會嘗試着去預實例化它。
Spring在運行時經過使用類型構造器(TypeBuilder )的API建立AOP代理。
兩種代理類型會被生成,基於結構(composition based )或者基於繼承(inheritance based)。若是目標對象實現了至少一個接口那麼就會生成基於結構的代理,否則就會生成基於繼承的代理。
基於結構的代理是經過生成一個實現全部目標對象的接口的類型來實現。這個動態類真實的名字是一個相似於」GUID「的東西。經過一個私有字段來保存目標對象的引用,而後這個動態類型的實現會在調用目標類以前或者以後執行通知。
基於繼承的代理會生成一個直接繼承目標類型的動態類型。這樣就能夠在須要的時候直接追溯到目標類。要注意的是,兩種方式中的目標方法實現若是調用了目標對象中的其餘方法,這個方法是不會被通知的。想強制使用基於繼承的代理方式,你能夠設置ProxyFactory 的ProxyTargetType 屬性爲true,或者使用將XML的名稱控件裏的proxy-target-type 設置true。
在.NET 2.0中你能夠定義程序集的特性InternalsVisibleTo來使得程序集內部的接口或者類容許被特定的」友好「的程序集訪問。若是你須要在一個內部的類或者接口生成一個AOP代理,須要在AssemblyInfo文件中加入如下代碼:[assembly: InternalsVisibleTo("Spring.Proxy")] and [assembly: InternalsVisibleTo("Spring.DynamicReflection")]。
在上文提到的基於繼承的代理有一個很大的限制,那就是全部的方法都要被聲明成虛方法。否則一些方法的調用會直接獲取私有的」目標「字段或者基類。winform對象就是這樣一個例子,它們的方法都不是虛方法。爲了解決這個問題,在1.2版本中引入了一個新的後處理機制(post-processing mechanism ),這種機制生成一個不包含私有的」目標「字段的代理類型。通知在調用基類方法以前直接被添加到方法體中。
想要使用這種新的基於繼承的代理,要在你的配置文件中聲明一個InheritanceBasedAopConfigurer實例,和一個IObjectFactoryPostProcessor,一個例子:
1 <object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop">
2 <property name="ObjectNames">
3 <list>
4 <value>Form*</value>
5 <value>Control*</value>
6 </list>
7 </property>
8 <property name="InterceptorNames">
9 <list>
10 <value>debugInterceptor</value>
11 </list>
12 </property>
13 </object>
14 <object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>
這個配置風格和自動代理很類似,尤爲是在你想爲WinForm類添加通知的時候很好用。
在Spring.NET中也很容易地經過代碼來建立AOP代理。這樣你就不須要依賴Spring.NET 的IoC功能。
如下展現瞭如何爲一個目標對象建立代理,其中包含一個攔截器和一個通知器。目標對象實現的接口會被自動代理:
1 ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); 2 factory.AddAdvice(myMethodInterceptor); 3 factory.AddAdvisor(myAdvisor); 4 IBusinessInterface tb = (IBusinessInterface) factory.GetProxy();
第一步是構建一個Spring.Aop.Framework.ProxyFactory對象。你能夠像上面的例子同樣建立一個目標對象,或者在構造器中指定要被代理的接口。
你能夠添加攔截器或者通知器,而後配置它們。
ProxyFactory中也有一些方便的方法來容許你添加其餘的通知類型。AdvisedSupport 是ProxyFactory 和ProxyFactoryObject的基類。
不管你經過那種方式建立AOP代理,你均可以經過使用Spring.Aop.Framework.IAdvised 來配置它們。全部的AOP代理均可以轉換成這個接口類型,不管它實現了其餘什麼接口。這個接口包括如下的方法和屬性:
1 public interface IAdvised 2 { 3 IAdvisor[] Advisors { get; } 4
5 IIntroductionAdvisor[] Introductions { get; } 6
7 void AddInterceptor(IInterceptor interceptor); 8
9 void AddInterceptor(int pos, IInterceptor interceptor); 10 void AddAdvisor(IAdvisor advisor); 11 void AddAdvisor(int pos, IAdvisor advisor); 12
13 void AddIntroduction(IIntroductionAdvisor advisor); 14
15 void AddIntroduction(int pos, IIntroductionAdvisor advisor); 16 int IndexOf(IAdvisor advisor); 17 int IndexOf(IIntroductionAdvisor advisor); 18 bool RemoveAdvisor(IAdvisor advisor); 19 void RemoveAdvisor(int index); 20
21 bool RemoveInterceptor(IInterceptor interceptor); 22 bool RemoveIntroduction(IIntroductionAdvisor advisor); 23
24 void RemoveIntroduction(int index); 25 void ReplaceIntroduction(int index, IIntroductionAdvisor advisor); 26 bool ReplaceAdvisor(IAdvisor a, IAdvisor b); 27 }
通知器屬性會被加入到工廠的全部通知器,攔截器或者其餘通知類型返回一個IAdvisor接口。若是你添加了一個IAdvisor,返回的通知器就將會是你添加的對象。若是你天界了一個攔截器或者其餘的通知類型,Spring.NET將會在一個通知器中使用一個老是返回true的IPointcut 接口包裝它。所以若是你添加了一個IMethodInterceptor,通知器就會返回一個DefaultPointcutAdvisor和一個匹配全部類型和方法的IPointcut 接口。
AddAdvisor() 方法能夠用來添加任意的IAdvisor接口。一般這個會是一個泛型DefaultPointcutAdvisor,這個通知器能夠和任何的通知或者切入點使用(除了引入通知)。
通常來講,一個代理被生成以後也能夠添加或者移除通知器或者攔截器。惟一的限制就是不能添加或者移除引入通知,所以工廠中的現存代理的接口不會改變。(你能夠從工廠獲取一個新的代理來避免這個問題)。
However you create AOP proxies, you can manipulate them using the Spring.Aop.Framework.IAdvised interface. Any AOP proxy can be cast to this interface, whatever other interfaces it implements. This interface includes the following methods and properties:
The Advisors property will return an IAdvisor for every advisor, interceptor or other advice type that has been added to the factory. If you added an IAdvisor, the returned advisor at this index will be the object that you added. If you added an interceptor or other advice type, Spring.NET will have wrapped this in an advisor with a IPointcut that always returns true. Thus if you added an IMethodInterceptor, the advisor returned for this
index will be a DefaultPointcutAdvisor returning your IMethodInterceptor and an IPointcut that matches all types and methods.
The AddAdvisor() methods can be used to add any IAdvisor. Usually this will be the generic DefaultPointcutAdvisor, which can be used with any advice or pointcut (but not for introduction).
By default, it's possible to add or remove advisors or interceptors even once a proxy has been created. The only restriction is that it's impossible to add or remove an introduction advisor, as existing proxies from the factory will not show the interface change. (You can obtain a new proxy from the factory to avoid this problem.) It's questionable whether it's advisable (no pun intended) to modify advice on a business object in production, although there are no doubt legitimate usage cases. However, it can be very useful in development: for example, in tests. I have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice, getting inside a method invocation I want to test. (For example, the advice can get inside a transaction created for that method: for example, to run SQL to check that a database was correctly updated, before marking the transaction for roll back.)
Depending on how you created the proxy, you can usually set a Frozen flag, in which case the IAdvised IsFrozen property will return true, and any attempts to modify advice through addition or removal will result in an AopConfigException. The ability to freeze the state of an advised object is useful in some cases: For example, to prevent calling code removing a security interceptor.