Castle 是 2003 年誕生於 Apache Avalon 項目,目的是爲了建立一個IOC 框架。發展到如今已經有四個組件:html
本文主要介紹 動態代理組件 Castle.DynamicProxygit
Castle.DynamicProxy 是經過 Emit 反射動態生成代理類來實現的,效率相對靜態植入要慢一點,但比普通的反射又高一些。動態代理只對公共接口方法、類中的虛方法生效,由於只有接口中的方法、類中的虛方法才能夠在子類中重寫。github
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(); } }
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<ProductRepository>(loggerIntercept); proxy.Add("大米"); }
在上例中,若是 ProductRepository.Add
不是虛方法,也不會報錯,可是攔截器不會被調用。c#
Castle.DynamicProxy 對異步函數的攔截跟同步沒啥差異,只是,若是要在方法執行完成後插入內容,須要 await
框架
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<TResult>
,或者不是異步函數,就會出錯。因此這裏是要對返回值進行一個判斷的。異步
可使用 Castle.Core.AsyncInterceptor 包,它包裝了 Castle,使異步調用更簡單。async
Castle.Core.AsyncInterceptor 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor函數
public class ProductRepository : IProductRepository { public Task Add(string name) { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"異步新增產品:{name}"); }); } public Task<string> 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<TResult>(IInvocation invocation) { invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation); Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id); } private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 異步執行前"); invocation.Proceed(); var task = (Task<TResult>)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
來確認。我的感受有點多此一舉。ui
public async void InterceptAsynchronous<TResult>(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 異步執行前"); invocation.Proceed(); var task = (Task<TResult>)invocation.ReturnValue; await task; Console.WriteLine($"{methodName} 異步執行完畢"); }
這樣就挺好的。this
若是有小夥伴知道爲何要返回一個新的 Task,請留言告訴我,謝謝!
Autofac.Extras.DynamicProxy 是一個 Autofac 擴展,可與 Castle 一塊兒提供 AOP 攔截。
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); //註冊要攔截的服務 builder.RegisterType<ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //啓用接口攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 IContainer container = builder.Build(); IProductRepository productRepo = container.Resolve<IProductRepository>(); productRepo.Add("大米"); }
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); //註冊要攔截的服務 builder.RegisterType<ProductRepository>() .EnableClassInterceptors() //啓用類攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 IContainer container = builder.Build(); ProductRepository productRepo = container.Resolve<ProductRepository>(); productRepo.Add("大米"); }
Castle.Core.AsyncInterceptor 中,IAsyncInterceptor
接口並不集成 IInterceptor
接口,而 Autofac.Extras.DynamicProxy 是綁定 Castle 的,因此按上面同步攔截的寫法是會報錯的。
IAsyncInterceptor
提供了 ToInterceptor()
擴展方法來進行類型轉換。
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<TResult>(IInvocation invocation) { //... } public void InterceptSynchronous(IInvocation invocation) { //... } } static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //註冊攔截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); builder.RegisterType<LoggerAsyncInterceptor>().AsSelf(); //註冊要攔截的服務 builder.RegisterType<ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //啓用接口攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 var container = builder.Build(); IProductRepository productRepo = container.Resolve<IProductRepository>(); productRepo.Get(); }
參考