首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

AOP 深度解析:從 Proxy 模式到 Attribute 注入的輕量級框架實作

     編輯:關於.NET

什麼是 AOP?

AOP (Aspect Oriented Programming,面向切面程式設計) 與 OOP 同為程式設計範式。核心思想:在不修改原有業務邏輯 (Original Logic) 的前提下,將可重用的橫切關注點 (Cross-cutting Concerns / Other Logic) 「注入」到目標方法執行流程中。

典型切面:

  • 交易管理 (Transaction)
  • 例外處理 / 記錄
  • 日誌審計
  • 身分驗證 / 授權
  • 效能監測
  • 快取攔截

AOP 將這些「散佈在各處、難以復用」的邏輯提取為獨立 Aspect (切面),實現 解耦 與 代碼簡潔化。

直觀對比:交易管理前後

傳統寫法:業務邏輯被樣板代碼淹沒

public void SaveRetireUserInfo(User user)
{
    using (TransactionScope transaction = new TransactionScope())
    {
        try
        {
            RetireUser retireUser = new RetireUser();
            retireUser.Name = user.Name;
            retireUser.Department = user.Department;
            // ...
            user.Delete();
            retireUser.Save();
            transaction.VoteCommit();
        }
        catch (Exception ex)
        {
            Log.Write(ex.Message);
            transaction.VoteRollBack();
        }
    }
}

AOP 版:業務還原純粹,切面透過 Attribute 注入

[AOPProxy(Interception = typeof(TransactionInterception))]
public class AOPTest : ContextBoundObject
{
    public void SaveRetireUserInfo(User user)
    {
        RetireUser retireUser = new RetireUser();
        retireUser.Name = user.Name;
        retireUser.Department = user.Department;
        // ...
        user.Delete();
        retireUser.Save();
        // 完全看不見 Transaction/Try-Catch!
    }
}

public class TransactionInterception : IInterception
{
    private TransactionScope transaction;
    public void PreInvoke()  { transaction = new TransactionScope(); }
    public void PostInvoke() { transaction.VoteCommit(); }
    public void ExceptionHandle() { transaction.VoteRollBack(); }
}

執行流程:

  1. PreInvoke() — 方法執行前
  2. 目標方法執行
  3. 若拋出例外 → ExceptionHandle();否則 → PostInvoke()

基石:Proxy 模式 (代理模式)

代理 = 中間層,保護真實主題 (Real Subject),在轉發呼叫時執行額外邏輯。

生活類比:你是巨星 SexyBaby,只負責唱歌;經紀人 Underdog 負責接洽、合約、行程。外界只能透過經紀人聯繫你。

技術實例:線上 Proxy 繞過封鎖

  • MyComputer:客戶端
  • ISite:網站抽象介面 (Url, ShowInformation())
  • AVSite:被封鎖的目標網站
  • CnblogsSite:正常網站
  • MyProxySite:代理網站 (部署於無封鎖國家),實作 ISite,內部持有真實 ISite
  • DNS:依 URL 解析回傳對應 ISite 實例
  • IFilter:網路過濾器介面 (Parse(ISite))
      - ChinaDNSFilter:攔截 AVSite → 回傳 WarnningSite
      - USADNSFilter:放行所有

關鍵程式碼片段:

// MyProxySite.ShowInformation()
Console.WriteLine("Proxy start!");
Console.WriteLine("Real url:{0}", _site.Url);
_site = new USADNSFilter().Parse(_site); // 換用美規則過濾
_site.ShowInformation(); // 真正顯示 AVSite 內容
Console.WriteLine("Proxy end!");
直接訪問被阻擋
透過 Proxy 成功訪問
正常網站訪問

AOP 框架核心實作

1. 切面攔截介面

public interface IInterception
{
    void PreInvoke();        // 方法前
    void PostInvoke();       // 方法後 (無例外)
    void ExceptionHandle();  // 方法拋出例外
}

2. Null Object 模式:NullInterception

public class NullInterception : IInterception
{
    public virtual void PreInvoke() { }
    public virtual void PostInvoke() { }
    public void ExceptionHandle() { }
}

避免 Null 檢查,預設「不做任何事」。

3. 核心:AOPRealProxy 繼承 RealProxy

利用 System.Runtime.Remoting.Messaging.IMessage 攔截訊息,類似 MFC Hook。

class AOPRealProxy : RealProxy, IProxyDI
{
    private MarshalByRefObject _target;
    private IInterception _interception = new NullInterception();

    public AOPRealProxy(Type targetType, MarshalByRefObject target) : base(targetType)
    {
        _target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        IMethodReturnMessage methodReturnMessage = null;
        IMethodCallMessage methodCallMessage = msg as IMethodCallMessage;
        if (methodCallMessage != null)
        {
            IConstructionCallMessage ccm = methodCallMessage as IConstructionCallMessage;
            if (ccm != null) // 建構子
            {
                RealProxy defaultProxy = RemotingServices.GetRealProxy(_target);
                defaultProxy.InitializeServerObject(ccm);
                methodReturnMessage = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)GetTransparentProxy());
            }
            else // 一般方法
            {
                _interception.PreInvoke();
                try
                {
                    methodReturnMessage = RemotingServices.ExecuteMessage(_target, methodCallMessage);
                }
                catch { }
                if (methodReturnMessage.Exception != null)
                    _interception.ExceptionHandle();
                else
                    _interception.PostInvoke();
            }
        }
        return methodReturnMessage;
    }

    public void InterceptionDI(IInterception interception) { _interception = interception; }
}

4. 介面注入:IProxyDI

interface IProxyDI { void InterceptionDI(IInterception interception); }

5. 兩種使用方式

方式一:Proxy Factory (程式碼式)

public class ProxyFactory
{
    public static T CreateProxyInstance<T>(IInterception interception) where T : new()
    {
        Type serverType = typeof(T);
        MarshalByRefObject target = Activator.CreateInstance(serverType) as MarshalByRefObject;
        AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target);
        aopRealProxy.InterceptionDI(interception);
        return (T)aopRealProxy.GetTransparentProxy();
    }
}

// 使用
IInterception interception = new MyInterception();
var proxy = ProxyFactory.CreateProxyInstance<AOPTestWithProxyClass>(interception);
proxy.Show();

方式二:Attribute (聲明式) — 繼承 ProxyAttribute

[AttributeUsage(AttributeTargets.Class)]
public class AOPProxyAttribute : ProxyAttribute
{
    private IInterception _interception = new NullInterception();

    public Type Interception
    {
        get { return _interception.GetType(); }
        set { _interception = Activator.CreateInstance(value) as IInterception; }
    }

    public override MarshalByRefObject CreateInstance(Type serverType)
    {
        MarshalByRefObject target = base.CreateInstance(serverType);
        AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target);
        aopRealProxy.InterceptionDI(_interception);
        return aopRealProxy.GetTransparentProxy() as MarshalByRefObject;
    }
}

// 使用:目標類別必須繼承 ContextBoundObject
[AOPProxy(Interception = typeof(MyInterception))]
public class AOPTestWithAttribute : ContextBoundObject
{
    public void Show() { Console.WriteLine("Hello, I am AOPTest."); }
}

// 直接 new 即可,Framework 自動攔截
var obj = new AOPTestWithAttribute();
obj.Show();

完整測試驗證

測試攔截器

public class MyInterception : IInterception
{
    public void PreInvoke()  { Console.WriteLine("===Pre MyInterception.==="); }
    public void PostInvoke() { Console.WriteLine("===Post MyInterception.==="); }
    public void ExceptionHandle() { Console.WriteLine("There is a exception!"); }
}

輸出結果

Proxy 模式輸出
Attribute 模式輸出

兩種方式均正確輸出 Pre/Post 環繞。

交易切面實戰測試

[AOPProxy(Interception = typeof(TransactionInterception))]
public class AOPTestWithAttributForTrasaction : ContextBoundObject
{
    public void ExecuteTransactionSuccessfully() { Console.WriteLine("Execute successfully :)"); }
    public void ExecuteTransactionUnsuccessfully() { Console.WriteLine("Execute unsuccessfully :("); throw new AOPNullException(); }
}

public class TransactionInterception : IInterception
{
    public void PreInvoke()  { Console.WriteLine("☆☆☆Begin transaction☆☆☆"); }
    public void PostInvoke() { Console.WriteLine("☆☆☆Commit transaction☆☆☆"); }
    public void ExceptionHandle() { Console.WriteLine("☆☆☆Rollback transaction☆☆☆"); }
}
交易提交/回滾輸出

成功路徑:Begin → Commit;失敗路徑:Begin → Rollback + ExceptionHandle。

> 💡 **注意**:Debug 模式下 throw 會中斷,按 F5 繼續即可;Release 或直接執行 exe 不受影響。

架構優缺點與適用場景

維度Proxy FactoryAttribute (ContextBoundObject)
使用方式程式碼建立 Proxy 實例聲明式,直接 new
目標類別限制繼承 MarshalByRefObject繼承 ContextBoundObject (更重)
效能較輕量Context 邊界轉換開銷較大
適用場景局部、動態決定是否 AOP全域、框架級、慣用模式
現代替代方案Castle DynamicProxy / Autofac Interception / PostSharp同上

總結

AOP 本質是 「橫切邏輯的外部化與注入」。本文展示的輕量框架基於 RealProxy + 訊息攔截,雖非生產級 (受限於 ContextBoundObject 效能與功能),但完整呈現了:

  1. IInterception 定義切面契約
  2. NullInterception 提供安全預設
  3. AOPRealProxy 實作訊息攔截與環繞調用
  4. ProxyAttribute 實現聲明式注入
  5. ProxyFactory 提供程式碼式彈性

現代 .NET 專案建議採用 Castle DynamicProxy (輕量、無 Context 限制) 或 PostSharp (編織期 IL 修改、零執行期 Proxy 開銷)。理解本文核心機制,有助於深入掌握各類 AOP 框架的底層運作原理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved