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:AutofacAutofac.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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏圣杰的专栏

ABP入门系列(7)——分页实现

完成了任务清单的增删改查,咱们来讲一讲必不可少的的分页功能。 首先很庆幸ABP已经帮我们封装了分页实现,实在是贴心啊。 来来来,这一节咱们就来捋一捋如何使用...

2275
来自专栏晓晨的专栏

ASP.NET Core 中间件(Middleware)详解

1062
来自专栏菩提树下的杨过

Flash/Flex学习笔记(4):如何打开网页及Get/Post数据

flash终究只是客户端技术,所以很多时候还是需要与服务端技术(比如asp,asp.net,jsp,php之类)进行数据交互的,下面的代码演示了如何在flash...

1847
来自专栏大内老A

如何利用ETW(Event Tracing for Windows)记录日志

ETW是Event Tracing for Windows的简称,它是Windows提供的原生的事件跟踪日志系统。由于采用内核(Kernel)层面的缓冲和日志记...

2525
来自专栏大内老A

WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance

我们知道WCF有3种典型的对service instance进行实例化的方式,他们分别与WCF的三种InstanceContextMode相匹配,他们分别是Pe...

1828
来自专栏DOTNET

.Net多线程编程—误用点分析

1 共享变量问题 错误写法: 所有的任务可能会共享同一个变量,所以输出结果可能会一样。 1 public static void Error() 2 { 3 ...

3028
来自专栏雨过天晴

原 HttpHelper两种代理方式

1434
来自专栏GuZhenYin

ASP.NET MVC Autofac依赖注入的一点小心得(包含特性注入)

前言 IOC的重要性 大家都清楚..便利也都知道..新的ASP.NET Core也大量使用了这种手法.. 一直憋着没写ASP.NET Core的文章..还是怕误...

21010
来自专栏Google Dart

Dart服务器端 shelf包 原

handler是处理shelf.Request并返回shelf.Response的任何函数。它可以处理请求本身 - 例如,在文件系统上查找请求的URI的静态文件...

1031
来自专栏木宛城主

ASP.NET那点不为人知的事(一)

我们上网时,在浏览器地址输入网址,按下回车,一张网页就呈现在我们眼前。这究竟发生了什么?对于一名优秀的Programmer来说,我想有必要一下熟悉浏览器---...

3018

扫码关注云+社区