前言
Autofac的DynamicProxy來(lái)自老牌的Castle項(xiàng)目。DynamicProxy(以下稱為動(dòng)態(tài)代理)起作用主要是為我們的類生成一個(gè)代理類,這個(gè)代理類可以在我們調(diào)用原本類的方法之前,調(diào)用攔截器以實(shí)現(xiàn)AOP。那么動(dòng)態(tài)代理是怎么實(shí)現(xiàn)的呢,這里簡(jiǎn)單一下提一下,這里主要是用了emit技術(shù)動(dòng)態(tài)生成IL,相當(dāng)于在內(nèi)存中用IL給我們編寫了一個(gè)Class。
通過(guò)靜態(tài)代理實(shí)現(xiàn)AOP
我們新建一個(gè)類Cat,并實(shí)現(xiàn)ICat接口
ICat:
public interface ICat
{
void Eat();
}
Cat:
public class Cat:ICat
{
public void Eat()
{
Console.WriteLine("貓?jiān)诔詵|西");
}
}
然然后我們?yōu)槠鋭?chuàng)建一個(gè)代理類,CatProxy
public class CatProxy:ICat
{
private readonly ICat _cat;
public CatProxy(ICat cat)
{
_cat = cat;
}
public void Eat()
{
Console.WriteLine("貓吃東西之前");
_cat.Eat();
Console.WriteLine("貓吃東西之后");
}
}
現(xiàn)在我們調(diào)用一下試試效果:
public class Progarm
{
static void Main(string[] args)
{
ICat icat=new Cat();
var catProxy=new CatProxy(icat);
catProxy.Eat();
Console.Read();
}
}

可以看見(jiàn),我們已經(jīng)成功的通過(guò)代理實(shí)現(xiàn)在貓吃東西之前和之后執(zhí)行我們定義的代碼,這就是一個(gè)簡(jiǎn)單的AOP,這個(gè)稱之為靜態(tài)代理,需要我們手動(dòng)編寫代理類,這個(gè)是十分耗費(fèi)時(shí)間的,那么有什么方法幫我們自動(dòng)生成代理呢,當(dāng)然有了,接下來(lái)介紹我們的動(dòng)態(tài)代理。
動(dòng)態(tài)代理(DynamicProxy)實(shí)現(xiàn)AOP
我在前言中已經(jīng)簡(jiǎn)單提了下動(dòng)態(tài)代理的實(shí)現(xiàn)原理,我們這里就只說(shuō)說(shuō)怎么用,而不去討論怎么實(shí)現(xiàn)了(燒腦闊)。我們這里使用Autofac的DynamicProxy。
我們依然使用前一章節(jié)所用的控制臺(tái)項(xiàng)目,通過(guò)nuget安裝兩個(gè)Package:Autofac、Autofac.Extras.DynamicProxy

首先我們需要定義一個(gè)攔截器:
public class CatInterceptor:IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("貓吃東西之前");
invocation.Proceed();
Console.WriteLine("貓吃東西之后");
}
}
然后在Autofac容器中注冊(cè)我們的攔截器和類型:
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<CatInterceptor>();//注冊(cè)攔截器
builder.RegisterType<Cat>().As<ICat>().InterceptedBy(typeof(CatInterceptor)).EnableInterfaceInterceptors();//注冊(cè)Cat并為其添加攔截器
var container = builder.Build();
var cat = container.Resolve<ICat>();
cat.Eat();
Console.Read();
}
我們運(yùn)行一下看看效果:

通過(guò)運(yùn)行我們可以看出,和上一章節(jié)的效果一樣,但是我們并不需要取手動(dòng)定義我們的代理類,而是通過(guò)組件動(dòng)態(tài)生成了。
關(guān)于這個(gè)攔截器,我們還可以通過(guò)Attribute的方式綁定到我們的具體類型,而不需要在注冊(cè)到容器的時(shí)候動(dòng)態(tài)指定。
[Intercept(typeof(CatInterceptor))]
public class Cat:ICat
{
public void Eat()
{
Console.WriteLine("貓?jiān)诔詵|西");
}
}
注冊(cè)的代碼可改為:
builder.RegisterType<Cat>().As<ICat>().EnableInterfaceInterceptors();
動(dòng)態(tài)代理的高級(jí)用法
我們前面說(shuō)了,動(dòng)態(tài)代理是動(dòng)態(tài)生成一個(gè)代理類,那么我們可以動(dòng)態(tài)的為這個(gè)代理類添加一個(gè)接口嗎,答案當(dāng)然是可以。
現(xiàn)在我們定義一個(gè)鏟屎官類:
public class CatOwner
{
}
可以看出我們的鏟屎官類什么都沒(méi)有,如果我們的鏟屎官想喂貓吃東西怎么辦,按照我們傳統(tǒng)的思維當(dāng)然是實(shí)例化一個(gè)cat傳入我們的CatOwner,但是我們可以用我們的DynamicProxy動(dòng)態(tài)生成。
var builder = new ContainerBuilder();
builder.RegisterType<CatInterceptor>();//注冊(cè)攔截器
builder.RegisterType<Cat>().As<ICat>();//注冊(cè)Cat
builder.RegisterType<CatOwner>().InterceptedBy(typeof(CatInterceptor))
.EnableClassInterceptors(ProxyGenerationOptions.Default, additionalInterfaces: typeof(ICat));//注冊(cè)CatOwner并為其添加攔截器和接口
var container = builder.Build();
var cat = container.Resolve<CatOwner>();//獲取CatOwner的代理類
cat.GetType().GetMethod("Eat").Invoke(cat, null);//因?yàn)槲覀兊拇眍愄砑恿薎Cat接口,所以我們可以通過(guò)反射獲取代理類的Eat方法來(lái)執(zhí)行
Console.Read();
我們上面的代碼是肯定不能運(yùn)行的,因?yàn)槲覀兊拇眍愲m然添加了ICat接口,但是卻沒(méi)有具體實(shí)現(xiàn)它,所以拋出為衛(wèi)視現(xiàn)異常:

我們可以使用AOP在我們執(zhí)行代理類的Eat方法之前去調(diào)用我們的具體實(shí)現(xiàn)Cat的Eat方法,我們修改一下攔截器。
public class CatInterceptor:IInterceptor
{
private readonly ICat _cat;
/// <summary>
/// 通過(guò)依賴注入 注入ICat的具體實(shí)現(xiàn)
/// </summary>
/// <param name="cat"></param>
public CatInterceptor(ICat cat)
{
_cat = cat;
}
public void Intercept(IInvocation invocation)
{
Console.WriteLine("喂貓吃東西");
invocation.Method.Invoke(_cat, invocation.Arguments);//調(diào)用Cat的指定方法
}
}
我們看一下運(yùn)行效果:

可以看見(jiàn)我們從一個(gè)什么都沒(méi)有的CatOwner類,來(lái)為其調(diào)用了一個(gè)具體的貓吃東西的行為,是不是感覺(jué)很神奇!
有人可能會(huì)說(shuō),一個(gè)鏟屎官為什么要去實(shí)現(xiàn)一個(gè)ICat接口。我想說(shuō)純屬胡編亂造,只是想闡明這個(gè)用法,這個(gè)意思。
應(yīng)用場(chǎng)景
用過(guò)ABP框架的人都應(yīng)該知道其有個(gè)技術(shù)名為DynamicWebapi,非常方便可以動(dòng)態(tài)幫我們的應(yīng)用邏輯層生成webapi,而不需要我們手動(dòng)去編寫webapi來(lái)發(fā)布。這里據(jù)用到了上面所說(shuō)的技術(shù),動(dòng)態(tài)生成Wabpi Controller,然后為其添加應(yīng)用邏輯接口,在調(diào)用具體的應(yīng)用邏輯方法時(shí)(Action)通過(guò)AOP攔截調(diào)用具體應(yīng)用邏輯實(shí)現(xiàn)來(lái)完成。
Demo:https://github.com/stulzq/BlogDemos/tree/master/AutofacDynamicProxyTest