Autofac高級(jí)用法之動(dòng)態(tài)代理

前言

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();
    }
}
image

可以看見(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

image

首先我們需要定義一個(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)行一下看看效果:

image

通過(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)異常:

image

我們可以使用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)行效果:

image

可以看見(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容