前言
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
目前學習.NET Core 最好的教程 .NET Core 官方教程 ASP.NET Core 官方教程
.NET Core 交流群:923036995 歡迎加群交流
如果您認為這篇文章還不錯或者有所收獲,您可以點選右下角的【推薦】支援,或請我喝杯咖啡【贊賞】,這将是我繼續寫作,分享的最大動力!
作者:曉晨Master(李志強)
聲明:原創部落格請在轉載時保留原文連結或者在文章開頭加上本人部落格位址,如發現錯誤,歡迎批評指正。凡是轉載于本人的文章,不能設定打賞功能,如有特殊需求請與本人聯系!