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 是通過(guò) Emit 反射動(dòng)態(tài)生成代理類來(lái)實(shí)現(xiàn)的,效率相對(duì)靜態(tài)植入要慢一點(diǎn),但比普通的反射又高一些。動(dòng)態(tài)代理只對(duì)公共接口方法、類中的虛方法生效,因?yàn)橹挥薪涌谥械姆椒?、類中的虛方法才可以在子類中重?xiě)。
基于接口的攔截器
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 對(duì)異步函數(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è)寫(xiě)法是簡(jiǎn)單粗暴的,如果碰到返回值是 Task<TResult>,或者不是異步函數(shù),就會(huì)出錯(cuò)。所以這里是要對(duì)返回值進(jìn)行一個(gè)判斷的。
可以使用 Castle.Core.AsyncInterceptor 包,它包裝了 Castle,使異步調(diào)用更簡(jiǎn)單。
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 提供的示例寫(xiě)法,這里有個(gè)問(wèn)題,也是我的疑惑。invocation.ReturnValue = InternalInterceptAsynchronous(invocation); 將導(dǎo)致代理返回的 Task 是一個(gè)新的 Task,這一點(diǎn)我們可以輸出 Task.Id 來(lái)確認(rèn)。個(gè)人感覺(jué)有點(diǎn)畫(huà)蛇添足。
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,請(qǐng)留言告訴我,謝謝!
Autofac 集成
Autofac.Extras.DynamicProxy 是一個(gè) Autofac 擴(kuò)展,可與 Castle 一起提供 AOP 攔截。
基于接口的攔截器
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注冊(cè)攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
//注冊(cè)要攔截的服務(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();
//注冊(cè)攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
//注冊(cè)要攔截的服務(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 的,所以按上面同步攔截的寫(xiě)法是會(huì)報(bào)錯(cuò)的。
IAsyncInterceptor 提供了 ToInterceptor() 擴(kuò)展方法來(lái)進(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();
//注冊(cè)攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();
//注冊(cè)要攔截的服務(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的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)對(duì)數(shù)組進(jìn)行隨機(jī)排序類實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)對(duì)數(shù)組進(jìn)行隨機(jī)排序類,實(shí)例分析了C#數(shù)組與隨機(jī)數(shù)操作技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03
C#實(shí)現(xiàn)的簡(jiǎn)單整數(shù)四則運(yùn)算計(jì)算器功能示例
這篇文章主要介紹了C#實(shí)現(xiàn)的簡(jiǎn)單整數(shù)四則運(yùn)算計(jì)算器功能,涉及C#界面布局、事件響應(yīng)及數(shù)值運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
c# Invoke和BeginInvoke 區(qū)別分析
這篇文章主要介紹了c# Invoke和BeginInvoke 區(qū)別分析,需要的朋友可以參考下2014-10-10
C# List<T> Contains<T>()的用法小結(jié)
本篇文章主要是對(duì)C#中List<T> Contains<T>()的用法進(jìn)行了總結(jié)介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
C#設(shè)計(jì)模式之Observer觀察者模式解決牛頓童鞋成績(jī)問(wèn)題示例
這篇文章主要介紹了C#設(shè)計(jì)模式之Observer觀察者模式解決牛頓童鞋成績(jī)問(wèn)題,簡(jiǎn)單講述了觀察者模式的原理并結(jié)合具體實(shí)例形式分析了使用觀察者模式解決牛頓童鞋成績(jī)問(wèn)題的具體步驟相關(guān)操作技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-09-09
C#記一次http協(xié)議multipart/form-data的boundary問(wèn)題
這篇文章主要介紹了C#記一次http協(xié)議multipart/form-data的boundary問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
Unity游戲開(kāi)發(fā)實(shí)現(xiàn)場(chǎng)景切換示例
這篇文章主要為大家介紹了Unity游戲開(kāi)發(fā)實(shí)現(xiàn)場(chǎng)景切換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

