C# 使用 Castle 實(shí)現(xiàn) AOP及如何用 Autofac 集成 Castle
Castle 是 2003 年誕生于 Apache Avalon 項(xiàng)目,目的是為了創(chuàng)建一個(gè)IOC 框架。發(fā)展到現(xiàn)在已經(jīng)有四個(gè)組件:
- ORM組件:ActiveRecord
- IOC組件:Windsor
- 動(dòng)態(tài)代理組件:DynamicProxy
- Web MVC組件:MonoRail
本文主要介紹 動(dòng)態(tài)代理組件 Castle.DynamicProxy
基本用法
Castle.DynamicProxy 是通過 Emit 反射動(dòng)態(tài)生成代理類來實(shí)現(xiàn)的,效率相對靜態(tài)植入要慢一點(diǎn),但比普通的反射又高一些。動(dòng)態(tài)代理只對公共接口方法、類中的虛方法生效,因?yàn)橹挥薪涌谥械姆椒ā㈩愔械奶摲椒ú趴梢栽谧宇愔兄貙憽?/p>
基于接口的攔截器
public interface IProductRepository { void Add(string name); } public class ProductRepository : IProductRepository { public void Add(string name) => Console.WriteLine($"新增產(chǎn)品:{name}"); } public class LoggerInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 執(zhí)行前"); //調(diào)用業(yè)務(wù)方法 invocation.Proceed(); Console.WriteLine($"{methodName} 執(zhí)行完畢"); } } 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($"新增產(chǎn)品:{name}"); } static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(); ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept); // 使用 CreateClassProxy 泛型方法可以省去實(shí)例化代碼 //ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept); proxy.Add("大米"); }
在上例中,如果 ProductRepository.Add 不是虛方法,也不會(huì)報(bào)錯(cuò),但是攔截器不會(huì)被調(diào)用。
異步函數(shù)攔截
Castle.DynamicProxy 對異步函數(shù)的攔截跟同步?jīng)]啥差別,只是,如果要在方法執(zhí)行完成后插入內(nèi)容,需要 await
public class ProductRepository { public virtual Task Add(string name) { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"異步新增產(chǎn)品:{name}"); }); } } public class LoggerInterceptor : IInterceptor { public async void Intercept(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 執(zhí)行前"); invocation.Proceed(); // 不 await 的話將會(huì)先輸出“執(zhí)行完畢”,再輸出“異步新增產(chǎn)品” var task = (Task)invocation.ReturnValue; await task; Console.WriteLine($"{methodName} 執(zhí)行完畢"); } }
上面這個(gè)寫法是簡單粗暴的,如果碰到返回值是 Task<TResult>,或者不是異步函數(shù),就會(huì)出錯(cuò)。所以這里是要對返回值進(jìn)行一個(gè)判斷的。
可以使用 Castle.Core.AsyncInterceptor 包,它包裝了 Castle,使異步調(diào)用更簡單。
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($"異步新增產(chǎn)品:{name}"); }); } public Task<string> Get() { return Task.Run(() => { Thread.Sleep(1000); Console.WriteLine($"獲取產(chǎn)品"); 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} 異步執(zhí)行前"); invocation.Proceed(); await (Task)invocation.ReturnValue; Console.WriteLine($"{methodName} 異步執(zhí)行完畢"); } 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} 異步執(zhí)行前"); invocation.Proceed(); var task = (Task<TResult>)invocation.ReturnValue; TResult result = await task; Console.WriteLine(task.Id); Console.WriteLine($"{methodName} 異步執(zhí)行完畢"); return result; } public void InterceptSynchronous(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 同步執(zhí)行前"); invocation.Proceed(); Console.WriteLine($"{methodName} 同步執(zhí)行完畢"); } } 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 提供的示例寫法,這里有個(gè)問題,也是我的疑惑。invocation.ReturnValue = InternalInterceptAsynchronous(invocation); 將導(dǎo)致代理返回的 Task 是一個(gè)新的 Task,這一點(diǎn)我們可以輸出 Task.Id 來確認(rèn)。個(gè)人感覺有點(diǎn)畫蛇添足。
public async void InterceptAsynchronous<TResult>(IInvocation invocation) { var methodName = invocation.Method.Name; Console.WriteLine($"{methodName} 異步執(zhí)行前"); invocation.Proceed(); var task = (Task<TResult>)invocation.ReturnValue; await task; Console.WriteLine($"{methodName} 異步執(zhí)行完畢"); }
這樣就挺好的。
如果有小伙伴知道為什么要返回一個(gè)新的 Task,請留言告訴我,謝謝!
Autofac 集成
Autofac.Extras.DynamicProxy 是一個(gè) Autofac 擴(kuò)展,可與 Castle 一起提供 AOP 攔截。
基于接口的攔截器
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //注冊攔截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); //注冊要攔截的服務(wù) 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(); //注冊要攔截的服務(wù) builder.RegisterType<ProductRepository>() .EnableClassInterceptors() //啟用類攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 IContainer container = builder.Build(); ProductRepository productRepo = container.Resolve<ProductRepository>(); productRepo.Add("大米"); }
異步函數(shù)攔截
Castle.Core.AsyncInterceptor 中,IAsyncInterceptor 接口并不集成 IInterceptor 接口,而 Autofac.Extras.DynamicProxy 是綁定 Castle 的,所以按上面同步攔截的寫法是會(huì)報(bào)錯(cuò)的。
IAsyncInterceptor 提供了 ToInterceptor() 擴(kuò)展方法來進(jìn)行類型轉(zhuǎn)換。
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(); //注冊要攔截的服務(wù) builder.RegisterType<ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //啟用接口攔截 .InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器 var container = builder.Build(); IProductRepository productRepo = container.Resolve<IProductRepository>(); productRepo.Get(); }
以上就是C# 使用 Castle 實(shí)現(xiàn) AOP及如何用 Autofac 集成 Castle的詳細(xì)內(nèi)容,更多關(guān)于C# 使用 Castle 實(shí)現(xiàn) AOP的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Unity實(shí)現(xiàn)卡片循環(huán)滾動(dòng)效果的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Unity實(shí)現(xiàn)卡片循環(huán)滾動(dòng)的效果,文中的實(shí)現(xiàn)步驟講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2022-12-12C#實(shí)現(xiàn)一個(gè)Word保護(hù)性模板文件
這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)一個(gè)Word保護(hù)性模板文件,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考一下2024-01-01C# 控件屬性和InitializeComponent()關(guān)系案例詳解
這篇文章主要介紹了C# 控件屬性和InitializeComponent()關(guān)系案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08關(guān)于C#中使用Oracle存儲(chǔ)過程返回結(jié)果集的問題
Oracle中可以使用游標(biāo)(Cursor)對數(shù)據(jù)集進(jìn)行操作,但在存儲(chǔ)過程輸出參數(shù)中直接使用Cursor錯(cuò)誤,下面小編給大家?guī)砹薈#中使用Oracle存儲(chǔ)過程返回結(jié)果集的問題,感興趣的朋友一起看看吧2021-10-10c# 動(dòng)態(tài)加載dll文件,并實(shí)現(xiàn)調(diào)用其中的方法(推薦)
下面小編就為大家?guī)硪黄猚# 動(dòng)態(tài)加載dll文件,并實(shí)現(xiàn)調(diào)用其中的方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02C#中動(dòng)態(tài)數(shù)組用法實(shí)例
這篇文章主要介紹了C#中動(dòng)態(tài)數(shù)組用法,實(shí)例分析了C#中ArrayList實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04