Autofac高級用法之動態代理
前言
Autofac的DynamicProxy來自老牌的Castle項目。DynamicProxy(以下稱為動態代理)起作用主要是為我們的類生成一個代理類,這個代理類可以在我們調用原本類的方法之前,調用攔截器以實現AOP。那麽動態代理是怎麽實現的呢,這裏簡單一下提一下,這裏主要是用了emit技術動態生成IL,相當於在內存中用IL給我們編寫了一個Class。
通過靜態代理實現AOP
我們新建一個類Cat
,並實現ICat
接口
ICat:
public interface ICat
{
void Eat();
}
Cat:
public class Cat:ICat { public void Eat() { Console.WriteLine("貓在吃東西"); } }
然然後我們為其創建一個代理類,CatProxy
public class CatProxy:ICat
{
private readonly ICat _cat;
public CatProxy(ICat cat)
{
_cat = cat;
}
public void Eat()
{
Console.WriteLine("貓吃東西之前");
_cat.Eat();
Console.WriteLine("貓吃東西之後");
}
}
現在我們調用一下試試效果:
public class Progarm
{
static void Main(string[] args)
{
ICat icat=new Cat();
var catProxy=new CatProxy(icat);
catProxy.Eat();
Console.Read();
}
}
可以看見,我們已經成功的通過代理實現在貓吃東西之前和之後執行我們定義的代碼,這就是一個簡單的AOP,這個稱之為靜態代理,需要我們手動編寫代理類,這個是十分耗費時間的,那麽有什麽方法幫我們自動生成代理呢,當然有了,接下來介紹我們的動態代理。
動態代理(DynamicProxy)實現AOP
我在前言中已經簡單提了下動態代理的實現原理,我們這裏就只說說怎麽用,而不去討論怎麽實現了(燒腦闊)。我們這裏使用Autofac的DynamicProxy。
我們依然使用前一章節所用的控制臺項目,通過nuget安裝兩個Package:Autofac
、Autofac.Extras.DynamicProxy
首先我們需要定義一個攔截器:
public class CatInterceptor:IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("貓吃東西之前");
invocation.Proceed();
Console.WriteLine("貓吃東西之後");
}
}
然後在Autofac容器中註冊我們的攔截器和類型:
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<CatInterceptor>();//註冊攔截器
builder.RegisterType<Cat>().As<ICat>().InterceptedBy(typeof(CatInterceptor)).EnableInterfaceInterceptors();//註冊Cat並為其添加攔截器
var container = builder.Build();
var cat = container.Resolve<ICat>();
cat.Eat();
Console.Read();
}
我們運行一下看看效果:
通過運行我們可以看出,和上一章節的效果一樣,但是我們並不需要取手動定義我們的代理類,而是通過組件動態生成了。
關於這個攔截器,我們還可以通過Attribute的方式綁定到我們的具體類型,而不需要在註冊到容器的時候動態指定。
[Intercept(typeof(CatInterceptor))]
public class Cat:ICat
{
public void Eat()
{
Console.WriteLine("貓在吃東西");
}
}
註冊的代碼可改為:
builder.RegisterType<Cat>().As<ICat>().EnableInterfaceInterceptors();
動態代理的高級用法
我們前面說了,動態代理是動態生成一個代理類,那麽我們可以動態的為這個代理類添加一個接口嗎,答案當然是可以。
現在我們定義一個鏟屎官
類:
public class CatOwner
{
}
可以看出我們的鏟屎官
類什麽都沒有,如果我們的鏟屎官想餵貓吃東西怎麽辦,按照我們傳統的思維當然是實例化一個cat傳入我們的CatOwner
,但是我們可以用我們的DynamicProxy動態生成。
var builder = new ContainerBuilder();
builder.RegisterType<CatInterceptor>();//註冊攔截器
builder.RegisterType<Cat>().As<ICat>();//註冊Cat
builder.RegisterType<CatOwner>().InterceptedBy(typeof(CatInterceptor))
.EnableClassInterceptors(ProxyGenerationOptions.Default, additionalInterfaces: typeof(ICat));//註冊CatOwner並為其添加攔截器和接口
var container = builder.Build();
var cat = container.Resolve<CatOwner>();//獲取CatOwner的代理類
cat.GetType().GetMethod("Eat").Invoke(cat, null);//因為我們的代理類添加了ICat接口,所以我們可以通過反射獲取代理類的Eat方法來執行
Console.Read();
我們上面的代碼是肯定不能運行的,因為我們的代理類雖然添加了ICat接口,但是卻沒有具體實現它,所以拋出為衛視現異常:
我們可以使用AOP在我們執行代理類的Eat方法之前去調用我們的具體實現Cat
的Eat方法,我們修改一下攔截器。
public class CatInterceptor:IInterceptor
{
private readonly ICat _cat;
/// <summary>
/// 通過依賴註入 註入ICat的具體實現
/// </summary>
/// <param name="cat"></param>
public CatInterceptor(ICat cat)
{
_cat = cat;
}
public void Intercept(IInvocation invocation)
{
Console.WriteLine("餵貓吃東西");
invocation.Method.Invoke(_cat, invocation.Arguments);//調用Cat的指定方法
}
}
我們看一下運行效果:
可以看見我們從一個什麽都沒有的CatOwner
類,來為其調用了一個具體的貓吃東西的行為,是不是感覺很神奇!
有人可能會說,一個鏟屎官為什麽要去實現一個ICat接口。我想說純屬胡編亂造,只是想闡明這個用法,這個意思。
應用場景
用過ABP框架的人都應該知道其有個技術名為DynamicWebapi,非常方便可以動態幫我們的應用邏輯層生成webapi,而不需要我們手動去編寫webapi來發布。這裏據用到了上面所說的技術,動態生成Wabpi Controller,然後為其添加應用邏輯接口,在調用具體的應用邏輯方法時(Action)通過AOP攔截調用具體應用邏輯實現來完成。
Demo:https://github.com/stulzq/BlogDemos/tree/master/AutofacDynamicProxyTest
Autofac高級用法之動態代理