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

Emit 實戰教學 (二):AOP 框架核心介面與屬性設計——切面契約、上下文物件與聲明式標記

     編輯:關於.NET

系列第二部分,定義 AOP 框架的三大切面介面、方法執行上下文物件、聲明式切面屬性,奠定動態代理生成與配置驅動的型別安全基礎。

三大切面介面契約

// 1. 前置處理:方法執行前攔截
public interface IPreProcess
{
    // 回傳 true 繼續執行原方法,false 短路直接返回
    bool PreProcess(MethodContext methodContext);
}

// 2. 後置處理:方法正常執行後攔截
public interface IPostProcess
{
    void PostProcess(MethodContext methodContext);
}

// 3. 例外處理:方法拋出例外時攔截
public interface IExceptionHandler
{
    void ProcessException(MethodContext methodContext, Exception exception);
}

介面設計理由

切面類型介面方法回傳值意義典型用途
IPreProcessbool PreProcess(ctx)true=繼續, false=短路 (需自行設定 ctx.Result)日誌、權限驗證、參數檢查、快取命中返回
IPostProcessvoid PostProcess(ctx)無 (ctx.Result 已含回傳值)日誌、審計、結果轉換、持久化
IExceptionHandlervoid ProcessException(ctx, ex)無 (可決定是否重拋或吞噬)錯誤日誌、重試、熔斷、友善錯誤包裝

MethodContext:方法執行時的完整上下文


public class MethodContext : MarshalByRefObject  // 支援跨 AppDomain/遠端
{
    private MethodInfo _methodInfo;
    public MethodInfo MethodInfo 
    { 
        get => _methodInfo; 
        set => _methodInfo = value; 
    }

    private object[] _arguments;
    public object[] Arguments 
    { 
        get => _arguments; 
        set => _arguments = value; 
    }

    private object _result;
    public object Result 
    { 
        get => _result; 
        set => _result = value; 
    }

    public MethodContext() { }
    
    public MethodContext(MethodInfo methodInfo, object[] parameters)
    {
        _methodInfo = methodInfo;
        _arguments = parameters;
    }
}

屬性設計考量

  • MethodInfo:提供方法完整中繼資料 (名稱、參數、回傳型別、屬性等)
  • Arguments:實際呼叫參數陣列,支援切面檢查/修改參數值
  • Result:方法回傳值 (PostProcess 階段已填入),支援結果攔截/修改
  • MarshalByRefObject:確保跨應用程式域/遠端呼叫時上下文正確傳遞

聲明式切面屬性:AspectAttribute

切面類型列舉 (支援位元組合)


[Flags]
public enum AspectTypes
{
    PreProcess = 1,      // 0b001
    ExceptionHandler = 2, // 0b010
    PostProcess = 4      // 0b100
}

屬性類別實作


[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AspectAttribute : Attribute
{
    private readonly AspectTypes _aspectType;
    
    public AspectTypes AspectType => _aspectType;
    
    public AspectAttribute(AspectTypes aspectType)
    {
        _aspectType = aspectType;
    }
}

使用範例:方法級聲明式切面標記


public class Test
{
    // 需要前置處理 + 例外處理
    [Aspect(AspectTypes.PreProcess | AspectTypes.ExceptionHandler)]
    public virtual void Test1(string num)
    {
        Console.WriteLine(Convert.ToInt32(num));
    }

    // 需要後置處理 + 例外處理
    [Aspect(AspectTypes.PostProcess | AspectTypes.ExceptionHandler)]
    public virtual int Test2(string num)
    {
        return Convert.ToInt32(num);
    }

    // 僅需要例外處理
    [Aspect(AspectTypes.ExceptionHandler)]
    public virtual int Test3(string num)
    {
        return Convert.ToInt32(num);
    }
}

反射檢測邏輯:位元運算判斷切面需求


// 取得方法上的 AspectAttribute
var attr = methodInfo.GetCustomAttribute<AspectAttribute>();
if (attr != null)
{
    var required = attr.AspectType;
    
    // 檢查是否需要 PreProcess
    if ((required & AspectTypes.PreProcess) == AspectTypes.PreProcess)
    {
        // 需植入前置切面...
    }
    
    // 檢查是否需要 PostProcess
    if ((required & AspectTypes.PostProcess) == AspectTypes.PostProcess)
    {
        // 需植入後置切面...
    }
    
    // 檢查是否需要 ExceptionHandler
    if ((required & AspectTypes.ExceptionHandler) == AspectTypes.ExceptionHandler)
    {
        // 需植入例外切面...
    }
}

配置檔與屬性的協作關係

層面AspectAttribute (程式碼)配置檔 (XML)
職責聲明方法需要哪類切面指定具體使用哪個實作類別
變更頻率開發期固定部署期可調整
型別安全編譯期檢查執行期反射解析
多實作選擇不支援支援 (id 映射不同實作)

工作流程:

  1. 掃描組件,反射找出標記 [Aspect(...)] 的虛擬方法
  2. 解析屬性得到 AspectTypes 位元遮罩
  3. 讀取配置檔 Application 區,匹配類別/方法簽名
  4. 對應 Registry 區解析具體實作型別 (或回退預設)
  5. Emit 生成代理類,按順序植入切面呼叫

動態代理建立入口


// 簡單工廠模式建立代理
public static class DynamicProxyFactory
{
    public static T CreateProxy<T>(Type targetType) where T : class
    {
        // 1. 檢查 targetType 是否為介面或含虛擬方法的類別
        // 2. 使用 ModuleBuilder/TypeBuilder 定義動態代理類型
        // 3. 實作介面/繼承目標類別
        // 4. 對每個虛擬方法生成代理方法體 (植入切面邏輯)
        // 5. CreateInstance 回傳代理實例
        return (T)Activator.CreateInstance(proxyType);
    }
}

// 使用方式
var test = DynamicProxyFactory.CreateProxy<Test>(typeof(Test));
test.Test1("123"); // 觸發 PreProcess → 原方法 → ExceptionHandler (若拋出)

設計模式總結

  • 介面隔離:三類切面各司其職,符合 ISP
  • 策略模式:切面實作可熱插拔,配置檔決定具體策略
  • 裝飾器模式:動態代理在原方法外層包裝切面邏輯
  • 責任鏈:同類切面多實作按配置順序串聯執行
  • 屬性導向編程:[Aspect] 標記切面需求,分離關注點

此設計在型別安全、聲明式配置、執行時彈性之間取得平衡,是輕量級 AOP 框架的經典架構藍圖。

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