C# Aspect-Oriented Programming(AOP) 利用多種模式實現動態代理
什麽是AOP(Aspect-Oriented Programming)?
AOP允許開發者動態地修改靜態的OO模型,構造出一個能夠不斷增長以滿足新增需求的系統,就象現實世界中的對象會在其生命周期中不斷改變自身,應用程序也可以在發展中擁有新的功能。
AOP利用一種稱為“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的行為封裝到一個可重用模塊,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,
卻為業務模塊所共同調用的邏輯或責任,例如事務處理、日誌管理、權限控制等,封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。
AOP技術本質
AOP(Aspect-Oriented Programming,面向方面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,
用以模擬公共行為的一個集合。當我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關系,但並不適合定義從左到右的關系。例如日誌功能。
日誌代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關系。對於其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散布在各處的無關的代碼被稱為
橫切(cross-cutting)代碼,在OOP設計中,它導致了大量代碼的重復,而不利於各個模塊的重用。
而AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行為封裝到一個可重用模塊,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,
就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關系,
如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行為;那麽面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的“方面”了。
然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。
使用“橫切”技術,AOP把軟件系統分為兩個部分:核心關註點和橫切關註點。業務處理的主要流程是核心關註點,與之關系不大的部分是橫切關註點。橫切關註點的一個特點是,他們經常發生在核心關註點的多處,
而各處都基本相似。比如權限認證、日誌、事務處理。Aop 的作用在於分離系統中的各種關註點,將核心關註點和橫切關註點分離開來。正如Avanade公司的高級方案構架師Adam Magee所說,
AOP的核心思想就是“將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。”
實現AOP的技術,主要分為兩大類:一是采用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行為的執行;二是采用靜態織入的方式,引入特定的語法創建“方面”,從而使得編譯器可以在編譯期間織入有
關“方面”的代碼。然而殊途同歸,實現AOP的技術特性卻是相同的,分別為:
1、join point(連接點):是程序執行中的一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不需要去定義一個join point。
2、point cut(切入點):本質上是一個捕獲連接點的結構。在AOP中,可以定義一個point cut,來捕獲相關方法的調用。
3、advice(通知):是point cut的執行代碼,是執行“方面”的具體邏輯。
4、aspect(方面):point cut和advice結合起來就是aspect,它類似於OOP中定義的一個類,但它代表的更多是對象間橫向的關系。
5、introduce(引入):為對象引入附加的方法或屬性,從而達到修改對象結構的目的。有的AOP工具又將其稱為mixin。
上述的技術特性組成了基本的AOP技術,大多數AOP工具均實現了這些技術。它們也可以是研究AOP技術的基本術語。
1.C# 手動實現實現靜態代理 <AOP在方法前後增加自定義的方法>
假設一個場景, 當用戶在系統登錄, 現在需要在登錄前分別做一個版本校驗, 和用戶登錄緩存處理... <利用裝飾者模式實現靜態代理實現登錄前後增加自定義方法>
/// <summary> /// 裝飾器模式實現靜態代理 /// AOP在方法前後增加自定義的方法 /// </summary> public class Decorator { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; IUserProcessor processor = new UserProcessor(); processor = new UserProcessorDecorator(processor); processor.RegUser(user); } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine("用戶登錄, Name:{0},PassWord:{1}", user.Name, user.Password); } } /// <summary> /// 裝飾器的模式去提供一個AOP功能 /// </summary> public class UserProcessorDecorator : IUserProcessor { private IUserProcessor UserProcessor { get; set; } public UserProcessorDecorator(IUserProcessor userprocessor) { UserProcessor = userprocessor; } public void RegUser(User user) { PreProceed(user); this.UserProcessor.RegUser(user); PostProceed(user); } public void PreProceed(User user) { Console.WriteLine("檢查軟件版本信息..."); } public void PostProceed(User user) { Console.WriteLine("保存本地緩存..."); } } }
如果沒有使用AOP, 上面的需求我們要怎麽去實現, 唯一的方法就是在執行方法的前後分別新增版本信息檢查, 和本地緩存處理。
public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; IUserProcessor processor = new UserProcessor(); Console.WriteLine("檢查軟件版本信息..."); processor.RegUser(user); Console.WriteLine("保存本地登錄緩存..."); }
2.使用Castle\DynamicProxy 實現動態代理
using Castle.DynamicProxy;//Castle.Core
/// <summary> /// 使用Castle\DynamicProxy 實現動態代理 /// </summary> public class CastleProxy { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; ProxyGenerator generator = new ProxyGenerator(); MyInterceptor interceptor = new MyInterceptor(); UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor); userprocessor.RegUser(user); } public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { PreProceed(invocation); invocation.Proceed(); PostProceed(invocation); } public void PreProceed(IInvocation invocation) { Console.WriteLine("檢查軟件版本信息"); } public void PostProceed(IInvocation invocation) { Console.WriteLine("保存本地登錄緩存"); } } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : IUserProcessor { public virtual void RegUser(User user) { Console.WriteLine("用戶登錄。Name:{0},PassWord:{1}", user.Name, user.Password); } } }
3. 使用.Net Remoting/RealProxy 實現動態代理
/// <summary> /// 使用.Net Remoting/RealProxy 實現動態代理 /// </summary> public class Proxy { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; UserProcessor userprocessor = TransparentProxy.Create<UserProcessor>(); userprocessor.RegUser(user); } public class MyRealProxy<T> : RealProxy { private T tTarget; public MyRealProxy(T target) : base(typeof(T)) { this.tTarget = target; } public override IMessage Invoke(IMessage msg) { PreProceede(msg); IMethodCallMessage callMessage = (IMethodCallMessage)msg; object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args); PostProceede(msg); return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); } public void PreProceede(IMessage msg) { Console.WriteLine("檢查軟件版本信息"); } public void PostProceede(IMessage msg) { Console.WriteLine("保存本地登錄緩存"); } } //TransparentProxy public static class TransparentProxy { public static T Create<T>() { T instance = Activator.CreateInstance<T>(); MyRealProxy<T> realProxy = new MyRealProxy<T>(instance); T transparentProxy = (T)realProxy.GetTransparentProxy(); return transparentProxy; } } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : MarshalByRefObject, IUserProcessor { public void RegUser(User user) { Console.WriteLine("用戶登錄。用戶名稱{0} Password{1}", user.Name, user.Password); } } }
C# Aspect-Oriented Programming(AOP) 利用多種模式實現動態代理