1. 程式人生 > >使用 Castle 實現 AOP,以及 Autofac 整合 Castle

使用 Castle 實現 AOP,以及 Autofac 整合 Castle

Castle 是 2003 年誕生於 Apache Avalon 專案,目的是為了建立一個IOC 框架。發展到現在已經有四個元件: - ORM元件:ActiveRecord - IOC元件:Windsor - 動態代理元件:DynamicProxy - Web MVC元件:MonoRail 本文主要介紹 動態代理元件 **Castle.DynamicProxy** ### 基本用法 **Castle.DynamicProxy** 是通過 Emit 反射動態生成代理類來實現的,效率相對靜態植入要慢一點,但比普通的反射又高一些。**動態代理只對公共介面方法、類中的虛方法生效,因為只有介面中的方法、類中的虛方法才可以在子類中重寫。** #### 基於介面的攔截器 ```c# public interface IProductRepository { void Add(string name); } public class ProductRepository : IProductRepository { public void Add(string name) => Console.WriteLine($"新增產品:{name}"); } public class LoggerInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 執行前"); //呼叫業務方法 invocation.Proceed(); Console.WriteLine($"{methodName} 執行完畢"); } } class Program { static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(); IProductRepository productRepo = new ProductRepository(); IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept); proxy.Add("大米"); Console.Read(); } } ``` #### 基於類的攔截器 ```c# public class ProductRepository { public virtual void Add(string name) => Console.WriteLine($"新增產品:{name}"); } static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(); ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept); // 使用 CreateClassProxy 泛型方法可以省去例項化程式碼 //ProductRepository proxy = generator.CreateClassProxy(loggerIntercept); proxy.Add("大米"); } ``` 在上例中,如果 `ProductRepository.Add` 不是虛方法,也不會報錯,但是攔截器不會被呼叫。 #### 非同步函式攔截 **Castle.DynamicProxy** 對非同步函式的攔截跟同步沒啥差別,只是,如果要在方法執行完成後插入內容,需要 `await` ```c# public class ProductRepository { public virtual Task Add(string name) { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"非同步新增產品:{name}"); }); } } public class LoggerInterceptor : IInterceptor { public async void Intercept(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 執行前"); invocation.Proceed(); // 不 await 的話將會先輸出“執行完畢”,再輸出“非同步新增產品” var task = (Task)invocation.ReturnValue; await task; Console.WriteLine($"{methodName} 執行完畢"); } } ``` 上面這個寫法是簡單粗暴的,如果碰到返回值是 `Task`,或者不是非同步函式,就會出錯。所以這裡是要對返回值進行一個判斷的。 可以使用 **Castle.Core.AsyncInterceptor** 包,它包裝了 **Castle**,使非同步呼叫更簡單。 **Castle.Core.AsyncInterceptor** 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor ```c# public class ProductRepository : IProductRepository { public Task Add(string name) { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"非同步新增產品:{name}"); }); } public Task Get() { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"獲取產品"); return "大米"; }); } } public class LoggerInterceptor : IAsyncInterceptor { public void InterceptAsynchronous(IInvocation invocation) { invocation.ReturnValue = InternalInterceptAsynchronous(invocation); } async Task InternalInterceptAsynchronous(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 非同步執行前"); invocation.Proceed(); await (Task)invocation.ReturnValue; Console.WriteLine($"{methodName} 非同步執行完畢"); } public void InterceptAsynchronous(IInvocation invocation) { invocation.ReturnValue = InternalInterceptAsynchronous(invocation); Console.WriteLine(((Task)invocation.ReturnValue).Id); } private async Task InternalInterceptAsynchronous(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 非同步執行前"); invocation.Proceed(); var task = (Task)invocation.ReturnValue; TResult result = await task; Console.WriteLine(task.Id); Console.WriteLine($"{methodName} 非同步執行完畢"); return result; } public void InterceptSynchronous(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 同步執行前"); invocation.Proceed(); Console.WriteLine($"{methodName} 同步執行完畢"); } } class Program { static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IAsyncInterceptor loggerIntercept = new LoggerInterceptor(); IProductRepository productRepo = new ProductRepository(); IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept); proxy.Get(); } } ``` 這是 **Castle.Core.AsyncInterceptor** 提供的示例寫法,這裡有個問題,也是我的疑惑。`invocation.ReturnValue = InternalInterceptAsynchronous(invocation);` 將導致代理返回的 `Task` 是一個新的 `Task`,這一點我們可以輸出 `Task.Id` 來確認。個人感覺有點畫蛇添足。 ```c# public async void InterceptAsynchronous(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 非同步執行前"); invocation.Proceed(); var task = (Task)invocation.ReturnValue; await task; Console.WriteLine($"{methodName} 非同步執行完畢"); } ``` 這樣就挺好的。 如果有小夥伴知道為什麼要返回一個新的 Task,請留言告訴我,謝謝! ### Autofac 整合 **Autofac.Extras.DynamicProxy** 是一個 **Autofac** 擴充套件,可與 **Castle** 一起提供 AOP 攔截。 #### 基於介面的攔截器 ```c# static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType().AsSelf(); //註冊要攔截的服務 builder.RegisterType().AsImplementedInterfaces() .EnableInterfaceInterceptors() //啟用介面攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 IContainer container = builder.Build(); IProductRepository productRepo = container.Resolve(); productRepo.Add("大米"); } ``` #### 基於類的攔截器 ```c# static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType().AsSelf(); //註冊要攔截的服務 builder.RegisterType() .EnableClassInterceptors() //啟用類攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 IContainer container = builder.Build(); ProductRepository productRepo = container.Resolve(); productRepo.Add("大米"); } ``` #### 非同步函式攔截 **Castle.Core.AsyncInterceptor** 中,`IAsyncInterceptor` 介面並不整合 `IInterceptor` 介面,而 **Autofac.Extras.DynamicProxy** 是繫結 **Castle** 的,所以按上面同步攔截的寫法是會報錯的。 `IAsyncInterceptor` 提供了 `ToInterceptor()` 擴充套件方法來進行型別轉換。 ```c# public class LoggerInterceptor : IInterceptor { readonly LoggerAsyncInterceptor interceptor; public LoggerInterceptor(LoggerAsyncInterceptor interceptor) { this.interceptor = interceptor; } public void Intercept(IInvocation invocation) { this.interceptor.ToInterceptor().Intercept(invocation); } } public class LoggerAsyncInterceptor : IAsyncInterceptor { public void InterceptAsynchronous(IInvocation invocation) { //... } public void InterceptAsynchronous(IInvocation invocation) { //... } public void InterceptSynchronous(IInvocation invocation) { //... } } static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType().AsSelf(); builder.RegisterType().AsSelf(); //註冊要攔截的服務 builder.RegisterType().AsImplementedInterfaces() .EnableInterfaceInterceptors() //啟用介面攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 var container = builder.Build(); IProductRepository productRepo = container.Resolve(); productRepo.Get(); } ``` **參考** https://www.cnblogs.com/youring2/p/10962573.html