前幾天看見在項目中用Castle來實現(xiàn)動態(tài)代理黎侈,覺得很有意思灸芳,就找個時間自己研究一下涝桅。介紹就免了,那開始吧烙样。
public class AccountController : Controller
{
private AccountService accountService = new AccountService();
// GET: Account
public ActionResult Index(string name)
{
ViewBag.Msg = accountService.GetUser(name);
return View();
}
}
以前Service的實現(xiàn)基本上是IOC或者單例冯遂,今天我們就用動態(tài)代理來實現(xiàn)。
public ActionResult Index(string name)
{
var proxy = new ProxyGenerator(); //提供類和接口的代理對象
var interceptor =new AccountInterceptor(); //攔截器
//var logInterceptor = new LogInterceptor();
var accountService = proxy.CreateClassProxy<AccountService>(interceptor);
ViewBag.Msg = accountService.GetUser(name);
return View();
}
ProxyGenerator類會創(chuàng)建一個代理類來繼承目標(biāo)類误阻,并重寫目標(biāo)類中虛方法债蜜。
public class AccountInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var arguments = invocation.GetArgumentValue(0) as string;
if (string.IsNullOrWhiteSpace(arguments))
{
invocation.ReturnValue = "nono";
invocation.SetArgumentValue(0, "Stranger");
invocation.Proceed();
invocation.ReturnValue = "Stranger";
return;
}
else if (arguments.Equals("陳綺貞"))
{
invocation.SetArgumentValue(0,arguments+"SAMA!");
invocation.Proceed();
return;
}
invocation.Proceed();
return;
}
}
攔截器需要需要實現(xiàn)IInterceptor接口晴埂,實現(xiàn)Intercept方法。Intercept接受一個實現(xiàn)了IInvocation接口類的參數(shù)寻定,invocation個參數(shù)封裝了一個代理的方法儒洛,其中Proceed用于調(diào)用目標(biāo)方法±撬伲看一下方法注釋琅锻。
// 摘要:(度娘翻譯)
// Proceeds the call to the next interceptor in line, and ultimately to the target
// method.將調(diào)用到下一個攔截線,并最終向目標(biāo)方法向胡。
//
// 備注:
// Since interface proxies without a target don't have the target implementation
// to proceed to, it is important, that the last interceptor does not call this
// method, otherwise a System.NotImplementedException will be thrown.
沒有目標(biāo)的接口代理沒有目標(biāo)實現(xiàn)要繼續(xù)進行恼蓬,這是很重要的,即最后一個攔截不調(diào)用這個方法僵芹,否則會拋出 system.notimplementedexception处硬。
void Proceed();
解釋一下:ProxyGenerator的CreateClassProxy方法接受的是一個實現(xiàn)了IInterceptor接口的攔截器可變數(shù)組,并按順序執(zhí)行拇派,類似一個調(diào)用鏈荷辕。Proceed被調(diào)用時,若果當(dāng)前攔截器之后還有攔截器等待執(zhí)行件豌,則將調(diào)用傳遞到下一個攔截器疮方,而不會去調(diào)用需要執(zhí)行的方法,等到最后一個攔截器時才調(diào)用目標(biāo)方法茧彤。如果我們調(diào)用的方法不是虛方法骡显,也不會拋出異常的。(這里有個疑問:記得我在公司時曾掂,當(dāng)我們調(diào)用的方法不是虛函數(shù)時也能被攔截到惫谤,Proceed方法不調(diào)用目標(biāo)方法,然后攔截器結(jié)束后才會調(diào)用目標(biāo)方法遭殉,今天debug時卻發(fā)現(xiàn)攔截器一直沒進去石挂,不知道是不是由于使用的CreateClassProxy重載方法不同,明天去公司看一下险污。更新:雖然調(diào)用的方法不是虛函數(shù)痹愚,但是其內(nèi)部調(diào)用了一個虛函數(shù),這個虛函數(shù)被攔截到了蛔糯,而不是調(diào)用的方法被攔截到拯腮。)
IInvocation接口里有一些方法可以使我們獲取和設(shè)置目標(biāo)方法的參數(shù)可返回值,在這里我們就可以做一些參數(shù)上的驗證和日志的記錄蚁飒。
Castle的動態(tài)代理很簡單明了动壤,也是AOP實現(xiàn)方式的一種,有興趣的可以了解一下淮逻。
PS:有錯誤歡迎指摘琼懊。