系列第二部分,定義 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);
}
| 切面類型 | 介面方法 | 回傳值意義 | 典型用途 |
|---|---|---|---|
| IPreProcess | bool PreProcess(ctx) | true=繼續, false=短路 (需自行設定 ctx.Result) | 日誌、權限驗證、參數檢查、快取命中返回 |
| IPostProcess | void PostProcess(ctx) | 無 (ctx.Result 已含回傳值) | 日誌、審計、結果轉換、持久化 |
| IExceptionHandler | void ProcessException(ctx, ex) | 無 (可決定是否重拋或吞噬) | 錯誤日誌、重試、熔斷、友善錯誤包裝 |
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;
}
}
[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 映射不同實作) |
工作流程:
[Aspect(...)] 的虛擬方法AspectTypes 位元遮罩Application 區,匹配類別/方法簽名Registry 區解析具體實作型別 (或回退預設)
// 簡單工廠模式建立代理
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 (若拋出)
[Aspect] 標記切面需求,分離關注點此設計在型別安全、聲明式配置、執行時彈性之間取得平衡,是輕量級 AOP 框架的經典架構藍圖。