AOP 和 OOP,在我看來是兩種相輔相成的技術,作為OOP的補充,AOP 有著自己特殊的應用場景。
假設,我們需要在Service層實現以下幾項基本功能:
/// <para>1、自動管理數據庫連接[可選]</para>
/// <para>2、自動管理數據庫事務,當接收到異常后(無論什么異常)事務將自動回滾[可選]</para>
/// <para>3、服務級加鎖[必選]</para>
/// <para>4、以統一方式處理 服務異常 及 錯誤, 包括數據庫異常 和 主動拋出的異常[必選]</para>
解釋:
1、在 執行Service方法前 打開數據庫連接, 在 執行Service方法后 關閉數據庫連接
2、在 執行Service方法前 Begin數據庫事務, 在 執行Service方法后 Commit數據庫事務, Catch異常后 RollBack數據庫事務
3、將 整個Service方法 lock 進去,lock Service 的私有靜態對象,以達到服務級方法的 線程安全及同步工作
4、捕獲Service方法中所有未捕獲的異常,捕獲異常后,如果需要將自動關閉連接和回滾事務。并記錄異常信息。
即、主動報告錯誤時,只需要拋出異常即可。
為了 實現如上的功能,并能簡單方便實現,而且不打破現有的C#編碼規范。
所以,引入AOP、 使用 Attribute 為方法 指定增強對象,
以便在調用Service方法前,執行方法的前置增強(包括打開連接、開啟事務等)
在調用Service方法后,執行方法的后置增強(包括關閉連接、提交事務等)
及 對整個調用方法實現 Try...Catch異常捕獲 和 Lock 加鎖。
C# 引入了 PRoxy (代理)的概念,即 System.Runtime.Remoting.Proxies.RealProxy 提供了 代理的基本功能。利用該對象可以自己實現AOP編程。
RealProxy 可以可以為任何 “直接或間接繼承于 System.MarshalByRefObject” 的類型 提供代理。
RealProxy 可以為指定類型創建一個代理對象, 被創建的代理對象的類型 可以看做是 指定類型的 子類(但 被指定的類型可以是密封類)。
【PS: 看做子類,更容易理解,本質上為被創建的代理對象的類型 和 指定類型直接為 組合關系,并不是繼承關系 】
RealProxy 的工作原理:
假設:
T 為 需要被代理的類型, t 為對象
ProxyT 為 被創建的代理類型, proxyT 為對象
T 類型中存在 成員方法 Test();
ProxyT 繼承于 T【實際上不為繼承關系,應該為組合,為方便理解看做繼承關系】, ProxyT 同樣也存在方法 Test
當執行如下代碼時:
proxyT.Test();
.NET runtime 會自動調用 System.Runtime.Remoting.Proxies.RealProxy.Invoke(...)方法。
而該方法為抽象方法,自己重寫該方法,在方法內部調用 t.Test()。
在調用之前、執行前置增強;在調用之后、執行后置增強; 及 其他處理操作。
由此可實現 AOP 編程,織入增強。
自定義的RealProxy
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Runtime.Remoting.Messaging;using System.Runtime.Remoting.Proxies;namespace AOPDemo.Common{ public class DelayProxy<T> : RealProxy { private static object objLock = new object(); /// <summary> /// 被代理的對象 /// </summary> private T target; public DelayProxy(T target) : base(typeof(T)) { this.target = target; } public override IMessage Invoke(IMessage msg) { IMethodCallMessage callMessage = (IMethodCallMessage)msg; Console.WriteLine("方法被調用前"); Console.WriteLine("調用方法名:" + callMessage.MethodName); IMessage message = DelayProxyUtil.InvokeBeProxy(this.target, callMessage); Console.WriteLine("方法被調用后"); return message; } }}
輔助工具類
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Runtime.Remoting.Proxies;using System.Runtime.Remoting.Messaging;using System.Reflection;namespace AOPDemo.Common{ /// <summary> /// 延遲初始化代理工具類 /// </summary> public static class DelayProxyUtil { /// <summary> /// 調用被代理對象中方法,返回 被代理對象的 方法返回值 /// <para>支持 out ref 參數</para> /// </summary> /// <param name="target"></param> /// <param name="callMessage"></param> /// <returns></returns> public static IMessage InvokeBeProxy(object target, IMethodCallMessage callMessage) { var args = callMessage.Args; object returnValue = callMessage.MethodBase.Invoke(target, args); return new ReturnMessage(returnValue, args, args.Length, callMessage.LogicalCallContext, callMessage); } /// <summary> /// 向上層拋出異常 /// </summary> /// <param name="ex"></param> /// <param name="callMessage"></param> /// <returns></returns> public static IMessage ReturnExecption(Exception ex, IMethodCallMessage callMessage) { return new ReturnMessage(ex, callMessage); } /// <summary> /// 獲取對象的代理 /// </summary> /// <param name="type"></param> /// <param name="instance"></param> /// <param name="delay"></param> /// <returns></returns> public static object GetTransparentProxy(Type type, object instance) { Type tmpType = typeof(DelayProxy<>); tmpType = tmpType.MakeGenericType(type); RealProxy proxy = Activator.CreateInstance(tmpType, new object[] { instance }) as RealProxy; return proxy.GetTransparentProxy(); } }}
簡單的Demo
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { Service service = new Service(); Service proxy = Common.DelayProxyUtil.GetTransparentProxy(typeof(Service), service) as Service; proxy.Test(); return View(); } } public class Service : MarshalByRefObject { public void Test() { Console.WriteLine("調用Test方法"); } }
由于例子很簡單,就不上傳源碼了。
未完待續...
新聞熱點
疑難解答