難得在這樣一個節日里給寫出一篇博客,卻沒有佳人相約,沒辦法,這就是一個程(dan)序(shen)猿(gou)的真實生活情景,每天除了coding還是coding。唉..污染各位看官的眼了。好吧,進入正題,最近做的項目各種驗證,異常控制,特別的麻煩,剛好前幾天聽到一個同事說起AOP 然后就想學習下應用到項目中去,自己也是在網上找了很多的資料,之后發現 網上的大部分資料的內容幾乎都是如出一撤,於是自己就整理了一下再加上這幾天的學習總結到的知識寫到這篇文章中,首先帶大家了解一下AOP,AOP(面向切面編程)在Java中應該算比較常見的,.net呢很少有人談起吧,AOP主要用於做權限控制,日志,事物,攔截和記錄(本篇總結中我不會說太多有關事務的處理,這不是重點),我覺得AOP最大的不同其實是,在不增加代碼的基礎上,還增加新的功能,
在簡單的業務邏輯方法中我們可能是這樣寫的
方法()
{
邏輯處理.......
邏輯處理.......
邏輯處理.......
}
要是復雜的業務邏輯呢,可能會這樣寫
方法()
{
try(異常處理.......)
{
If(權限判斷.....)
記錄日志......
邏輯處理.......
邏輯處理.......
邏輯處理.......
}
catch(Exception e)
{
throwe;
記錄異常......
}
}
這樣代碼看起來就會很亂。
如果利用AOP對這些記錄做處理的話,應該是這樣
[判斷權限,記錄日志,事務,異常處理]
方法()
{
邏輯處理.......
邏輯處理.......
邏輯處理.......
}
這樣看起來是不是就清晰很多了。
好了,關於AOP的作用和分析呢就寫這么多,
下面開始一個簡單的AOP的創作過程吧
先把我整個項目的文件列表貼出來,大家只要看第一個和第四個項目就可以了,第二個和第三個只是我寫過放棄的項目 沒刪掉而已
首先創建一個控制台的項目 AOPconsole,然后緊接着創建一個名稱是NewAop的類庫
類庫創建完成后
我們在newaop中創建第一個接口文件 CommonDef.cs文件,文件中定義兩個接口IAopOperator
和IAopProxyFactory 代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting.Messaging; namespace NewAop { /// <summary> /// IAopOperator AOP操作符接口,包括前處理和后處理 /// </summary> public interface IAopOperator { void PreProcess(IMessage requestMsg); void PostProcess(IMessage requestMsg, IMessage Respond); } /// <summary> /// IAopProxyFactory 用於創建特定的Aop代理的實例,IAopProxyFactory的作用是使AopProxyAttribute獨立於具體的AOP代理類。 /// </summary> public interface IAopProxyFactory { AopProxyBase CreateAopProxyInstance(MarshalByRefObject obj, Type type); } }
接着我們創建一個比較重要的類MethodAopSwitcherAttribute.cs,這個類中的主要做自己定義的一些參數,比如事務 日志和執行的判斷條件,或者傳參都是可以的,這里是與網上其他的資料有些不同的地方,所以要注意細看,需要注意的幾點:1.該類中的屬性和字段,可以自己定義,我們利用這些字段去存儲目標方法要帶過來的一些參數或者其他的記錄,來做權限或者日志的判斷,2.重寫該類的構造函數,方便在使用的時候直接寫入參數,不用重新賦值(很省事對不對)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NewAop { /// <summary> /// MethodAopSwitcherAttribute 用於決定一個被AopProxyAttribute修飾的class的某個特定方法是否啟用截獲 。 /// 創建原因:絕大多數時候我們只希望對某個類的一部分Method而不是所有Method使用截獲。 /// 使用方法:如果一個方法沒有使用MethodAopSwitcherAttribute特性或使用MethodAopSwitcherAttribute(false)修飾, /// 都不會對其進行截獲。只對使用了MethodAopSwitcherAttribute(true)啟用截獲。 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class MethodAopSwitcherAttribute : Attribute { private int useAspect = 0; //記錄類型 private string userlog = ""; //記錄詳細信息 public MethodAopSwitcherAttribute(int useAop,string log) { this.useAspect = useAop; this.userlog = log; } public int UseAspect { get { return this.useAspect; } } public string Userlog { get { return this.userlog; } } } }
接着新建一個AopProxyBase.cs的抽象類文件,這個類中主要用於實現要做的日志,事物權限等等的判斷和目標方法的前后處理,該類需要注意的是,必須繼承RealProxy接口,同時需要引入命名空間。同時要寫接口中的Invoke方法。同時 在Invoke的方法中定義了兩個變量useAspect 和uselog ,這里的變量是用來記錄上一個類中定義的一些屬性值的,主要是為了方便調用,還有,在該類中可以看到有兩個抽象的方法PreProcess和PostProcess這個先不急到后面會細說,需要注意的是這段代碼RemotingServices.ExecuteMessage(this.target, call);這里是運行目標方法,並作返回值。這個時候可能你會覺得,如我不運行這段代碼直接返回一個NULL是不是就可以讓目標方法不運行了,其實你這樣寫可能你編譯正常,但在你運行的時候你會發現程序拋出異常了,其實這是一個重點,也許你可以用異常處理包裹你的目標方法中的代碼,但這樣會不會發現太不明智了,所以我們就需要使用 return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg);這段代碼來做返回值了,這樣寫既可以不讓你的返回值報錯,也可以保證你的目標方法中的代碼沒有被執行。好了這個類就說到這里 下面貼代碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Services; using System.Runtime.Remoting.Activation; namespace NewAop { /// <summary> /// AopProxyBase 抽象類 所有自定義AOP代理類都從此類派生,覆寫IAopOperator接口,實現具體的前/后處理 。 /// </summary> public abstract class AopProxyBase : RealProxy, IAopOperator { private readonly MarshalByRefObject target; //默認透明代理 #region IAopOperator 成員(兩個方法 一個在執行之前調用,另一個在執行之后調用,具體實現代碼寫在AopControlProxy類中) public abstract void PreProcess(IMessage requestMsg); //方法執行的預處理邏輯 public abstract void PostProcess(IMessage requestMsg, IMessage Respond); //方法執行結束的處理邏輯 #endregion public AopProxyBase(MarshalByRefObject obj, Type type) : base(type) { this.target = obj; } #region Invoke 重寫基方法 public override IMessage Invoke(IMessage msg) { int useAspect = 0; string uselog = ""; IMethodCallMessage call = (IMethodCallMessage)msg; //查詢目標方法是否使用了啟用AOP的MethodAopSwitcherAttribute foreach (Attribute attr in call.MethodBase.GetCustomAttributes(false)) { MethodAopSwitcherAttribute mehodAopAttr = attr as MethodAopSwitcherAttribute; if (mehodAopAttr != null) { useAspect = mehodAopAttr.UseAspect; uselog = mehodAopAttr.Userlog; break; //if (mehodAopAttr.UseAspect==1) //{ // useAspect = 1; // break; //} } } if (useAspect == 2) { this.PreProcess(msg); //執行方法之前的操作 } //如果觸發的是構造函數,此時target的構建還未開始 IConstructionCallMessage ctor = call as IConstructionCallMessage; if (ctor != null) { //獲取最底層的默認真實代理 RealProxy default_proxy = RemotingServices.GetRealProxy(this.target); default_proxy.InitializeServerObject(ctor); MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy(); //自定義的透明代理 this return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp); } if (useAspect ==2) { #region 若不想運行目標方法可以執行該代碼,如果直接return null會導致異常發生 IMethodCallMessage callMsg = msg as IMethodCallMessage; return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg); #endregion //#region 調用目標方法代碼 //IMethodMessage result_msg; //result_msg = RemotingServices.ExecuteMessage(this.target, call); //return result_msg; //#endregion } else { #region 調用目標方法代碼 IMethodMessage result_msg; result_msg = RemotingServices.ExecuteMessage(this.target, call); //IMethodReturnMessage result_msg = RemotingServices.ExecuteMessage(this.target, call); #endregion if (useAspect == 1) { this.PostProcess(msg, result_msg); //執行方法結束后的操作 } return result_msg; } } #endregion } }
接着我們創建一個新的文件AopProxyAttribute.cs,這里於其他資料中的沒有區別,不詳細說明
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting; using System.Runtime.Remoting.Proxies; namespace NewAop { /// <summary> /// AopProxyAttribute /// AOP代理特性,如果一個類想實現具體的AOP,只要實現AopProxyBase和IAopProxyFactory,然后加上該特性即可。 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AopProxyAttribute : ProxyAttribute { private IAopProxyFactory proxyFactory = null; public AopProxyAttribute(Type factoryType) { this.proxyFactory = (IAopProxyFactory)Activator.CreateInstance(factoryType); } #region 創建實例 /// <summary> /// 獲得目標對象的自定義透明代理 /// </summary> public override MarshalByRefObject CreateInstance(Type serverType)//serverType是被AopProxyAttribute修飾的類 { //未初始化的實例的默認透明代理 MarshalByRefObject target = base.CreateInstance(serverType); //得到位初始化的實例(ctor未執行) object[] args = { target, serverType }; //AopProxyBase rp = (AopProxyBase)Activator.CreateInstance(this.realProxyType ,args) ; //Activator.CreateInstance在調用ctor時通過了代理,所以此處將會失敗 //得到自定義的真實代理 AopProxyBase rp = this.proxyFactory.CreateAopProxyInstance(target, serverType);//new AopControlProxy(target ,serverType) ; return (MarshalByRefObject)rp.GetTransparentProxy(); } #endregion } }
接着創建AopControlProxyFactory.cs創建文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NewAop { public class AopControlProxyFactory : IAopProxyFactory { #region IAopProxyFactory 成員 public AopProxyBase CreateAopProxyInstance(MarshalByRefObject obj, Type type) { return new AopControlProxy(obj, type); } #endregion } }
接着最關鍵的地方到了,創建AopControlProxy.cs文件,該類繼承AopProxyBase類可以看到該類中我們實現了PreProcess和PostProcess兩個方法,兩個方法對應AopProxyBase類中的兩個抽象方法,分別用於做目標方法執行之前和之后的操作記錄,當然這里你可以繼續定義新的方法,如果要實現調用那就必須要在AopProxyBase類中也有對應的抽象方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting.Messaging; namespace NewAop { public class AopControlProxy:AopProxyBase { public AopControlProxy(MarshalByRefObject obj, Type type) : base(obj, type) //指定調用基類中的構造函數 { } public override void PreProcess(IMessage requestMsg) { Console.WriteLine("目標方法運行開始之前"); return; } public override void PostProcess(IMessage requestMsg, IMessage Respond) { Console.WriteLine("目標方法運行結束之后"); } } }
到此 一個用於方法代理的AOP算是完成了,那我們要怎么使用呢,暫時我只是在一個控制台系統中做了這AOP的使用實例,廢話不多說,代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NewAop; namespace AOPconsole { //[AopProxyAttribute(typeof(AopControlProxyFactory))] //將自己委托給AOP代理AopControlProxy class Program { static void Main(string[] args) { //try //{ Exameplec epc = new Exameplec("添加代理的方法"); epc.say_hello(); Console.WriteLine(""); Console.WriteLine("--------------------------這是分隔符--------------------------------"); Exameplec epcs = new Exameplec("未添加代理的方法"); epcs.sayByeBye(); Console.WriteLine("--------------------------這是分隔符--------------------------------"); //} //catch //{ // Console.WriteLine("報錯了"); //} Console.ReadLine(); } } //如果在類上添加該代碼 會導致沒有添加屬性的代碼也會被帶入Invoke中 //導致其他方法執行IMethodCallMessage callMsg = msg as IMethodCallMessage; return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg); //最終導致其他方法的代碼無法運行 [AopProxyAttribute(typeof(AopControlProxyFactory))] //將自己委托給AOP代理AopControlProxy,(最好不要添加該代碼) public class Exameplec : ContextBoundObject//放到特定的上下文中,該上下文外部才會得到該對象的透明代理 { private string name; public Exameplec(string a) { this.name = a; } [MethodAopSwitcherAttribute(2,"參數")] public void say_hello() { Console.WriteLine( name); } public void sayByeBye() { Console.WriteLine( name); } } }
可以看到,我在program中添加了一個新的方法Exameplec 這里只是為了方便觀看,如果要對方法進行AOP代理 那么需要在方法上加入[MethodAopSwitcherAttribute(2,"參數")]這當中的兩個參數就是在MethodAopSwitcherAttribute.cs中定義的兩個屬性,方便傳參,可以定義更多的屬性,比如你可以吧session當做參數傳入,盡量避免在AOP中進行一些服務端狀態或者會話的獲取。
一個完整的AOP案例算是完成了,總算這兩天的學習不是白費功夫啊,也感謝我在網上看到的各位大神給出的共享資料。
其實我對AOP還不是完全的了解,如果各位看客有什么疑問或者建議,還希望各位留言,你們的沒一個建議可能對我都是一次幫助,希望大家可以互相學習到更多的知識。