系列第三部分,實作 AOP 框架的配置檔案定義、解析管理器、方法級切面解析邏輯。配置檔採用聲明式結構,分離「切面實作註冊」與「目標方法套用規則」,支援預設切面與方法級覆寫。
<?xml version="1.0" encoding="utf-8" ?>
<AOP>
<!-- 切面實作註冊表:所有可用切面的類別對應 -->
<Registry>
<IPreProcess>
<Aspect id="AddPreProcessLog" value="YQL.TestProject:YQL.TestProject.AddPreProcessLog" isDefault="true" />
<Aspect id="VerifyPermission" value="YQL.AOP:YQL.AOP.VerifyPermission" />
</IPreProcess>
<IPostProcess>
<Aspect id="AddPostProcessLog" value="YQL.TestProject:YQL.TestProject.AddPostProcessLog" isDefault="true" />
</IPostProcess>
<IExceptionHandler>
<Aspect id="SimplyExceptionHandler" value="YQL.TestProject:YQL.TestProject.SimplyExceptionHandler" isDefault="true" />
</IExceptionHandler>
</Registry>
<!-- 應用規則:特定類別/方法套用哪些切面 -->
<Application>
<Class id="YQL.TestProject:YQL.TestProject.Test">
<Method id="Int32 Test4(System.String)">
<IPreProcess>
<Aspect id="AddPreProcessLog" />
</IPreProcess>
<IPostProcess>
<Aspect id="AddPostProcessLog" />
<Aspect id="TestException" />
</IPostProcess>
<IExceptionHandler>
<Aspect id="SimplyExceptionHandler" />
</IExceptionHandler>
</Method>
</Class>
</Application>
</AOP>
| 區段 | 用途 | 關鍵屬性 |
|---|---|---|
<Registry> | 切面實作型別登錄 | id (唯一鍵)、value (組件:完整型別名)、isDefault (預設切面旗標) |
<Application> | 目標方法切面套用規則 | 巢狀 Class → Method → 切面類型 → Aspect id 結構 |
設計意圖:未來可開發視覺化配置工具,透過反射掃描組件自動產生 Registry,開發者僅需在 Application 區勾選套用規則。
public static class AopConfigManager
{
private static Dictionary<string, AspectTag> _aspectInfoDict; // Registry 解析結果
private static Dictionary<string, ClassTag> _classInfoDict; // Application 解析結果
public static void Init(string configFilePath)
{
var document = new XPathDocument(configFilePath);
var navigator = document.CreateNavigator();
InitAspectInfo(navigator); // 解析 Registry
InitClassInfo(navigator); // 解析 Application
}
// XPathNavigator 遍歷 XML 節點,建構 AspectTag/ClassTag/MethodTag 物件樹
}
public static List<Type> GetMethodAspectInfo(MethodInfo methodInfo, AspectTypes aspectType)
{
string methodName = methodInfo.ToString(); // 含簽名: "Int32 Test4(System.String)"
string typeName = ReflectionHelper.GetTypeUniqueName(methodInfo.DeclaringType);
List<string> aspectIdList = null;
// 1. 嘗試從 Application 獲取方法專屬配置
if (_classInfoDict.TryGetValue(typeName, out var classTag))
{
aspectIdList = classTag.GetAspectInfoList(methodName, aspectType);
}
// 2. 回退至 Registry 預設切面
if (aspectIdList == null || aspectIdList.Count == 0)
{
string aspectName = EnumHelper.GetEnumDescription(aspectType);
var aspectTag = _aspectInfoDict[aspectName];
aspectIdList = aspectTag.GetDefaultAspectList();
}
// 3. 解析 id → 完整型別名 → Type
var typeNames = aspectIdList.Select(id => _aspectInfoDict[aspectName].GetAspectTypeUniqueName(id)).ToList();
return ConvertToTypeList(typeNames); // 反射載入組件、GetType
}
// 切面標籤:對應 Registry 中某切面類型 (IPreProcess 等) 的所有實作
class AspectTag
{
public Dictionary<string, string> AspectMap { get; } // id -> "Assembly:TypeName"
public List<string> DefaultAspectIds { get; }
public List<string> GetDefaultAspectList() => DefaultAspectIds;
public string GetAspectTypeUniqueName(string id) => AspectMap[id];
}
// 類別標籤:對應 Application 中某類別的方法切面規則
class ClassTag
{
public Dictionary<string, MethodTag> Methods { get; } // 方法簽名 -> MethodTag
public List<string> GetAspectInfoList(string methodName, AspectTypes aspectType)
=> Methods.TryGetValue(methodName, out var mt) ? mt.GetAspectIds(aspectType) : null;
}
// 方法標籤:單一方法在三類切面上的具體套用 id 清單
class MethodTag
{
public Dictionary<AspectTypes, List<string>> AspectIds { get; }
public List<string> GetAspectIds(AspectTypes type)
=> AspectIds.TryGetValue(type, out var list) ? list : null;
}
AopConfigManager.Init("aop.config")GetMethodAspectInfo(method, AspectTypes.PreProcess)List<Type>,透過 Activator.CreateInstance 或容器解析建立切面實例public virtual int Test4(string arg)
{
var context = new MethodContext(methodInfo, new object[]{arg});
try
{
// PreProcess 切面鏈
if (preProcessAspects.All(a => a.PreProcess(context)))
{
// 原始方法體
var result = base.Test4(arg);
context.Result = result;
// PostProcess 切面鏈
postProcessAspects.ForEach(a => a.PostProcess(context));
return result;
}
return default;
}
catch (Exception ex)
{
// ExceptionHandler 切面鏈
exceptionAspects.ForEach(a => a.ProcessException(context, ex));
throw; // 或根據切面決定是否吞噬
}
}
「這只是一個極簡 AOP 框架,主要為了練習 Emit 動態代理生成、配置驅動架構、切面鏈管線設計。生產環境建議使用成熟方案:Castle DynamicProxy、PostSharp、AspectCore、.NET Core 內建攔截器。」
Task PreProcessAsync(...) 配合 awaitFileSystemWatcher 監聽配置變更自動重新解析System.Diagnostics.Activity 實現分散式追蹤掌握「配置驅動 + 反射解析 + Emit 動態代理」三位一體架構,是理解現代 AOP 框架核心運作機制的關鍵路徑。