Autofac 之 基于 Castle DynamicProxy2 的 Interceptor 功能

来源:互联网 发布:石家庄盛光网络 编辑:程序博客网 时间:2024/06/10 10:07

Autofac 结合 Castle DynamicProxy2 功能

 

     Autofac 不仅作为轻量级高效的 IoC 容器,而且还能很好的与 Castle.DynamicProxy2 结合起来,实现 AOP 功能。

     首先,我们需要定义拦截器,简单的定义可实现 Castle.DynamicProxy.IInterceptor 接口即可。

 

添加拦截器

 

     定义好了拦截器后,如何应用到相关对象呢?有两种方式:

     1)使用 Autofac.Extras.DynamicProxy2.InterceptAttribute 特性将拦截器添加到相关的类或接口上;

     2)使用 ContainerBuilder 的 InterceptedBy() 方法在注册对象的同时添加拦截器。

 

     如何启用拦截器呢?我们只需要在 IoC 注册启用即可。启用拦截器有三种方式,其中针对 WCF 比较特别,下面来具体分析这几种方式的应用场景:

 

基于接口的拦截器

 

     在注册对象的同时启用 EnableInterfaceInterceptors() 方法。

     使用接口的拦截器,在使用特性 [Attribute] 注册时,注册拦截器可注册到接口(Interface)上或其实现类(Implement)上。使用注册到接口上方式,所有的实现类都能应用到拦截器。

     对于以接口方式的注入,Autofac Interceptor 要求类的方法为 public 或 virtual 即可。

     

示例代码:

var builder = new ContainerBuilder();

builder.RegisterType<SomeType>()

       .As<ISomeInterface>()

       .EnableInterfaceInterceptors();

builder.Register(c => new CallLogger(Console.Out));

var container = builder.Build();

var willBeIntercepted = container.Resolve<ISomeInterface>();


于类的拦截器

 

     在注册对象的同时启用 EnableClassInterceptors() 方法。

     对于以类方式的注入,Autofac Interceptor 要求类的方法为必须为 virtual 方法。

     值得注意的是:对于 子类,重写(override)父类的虚方法时,能应用到拦截器。父类可在 IoC 中注册也可不需要注册,但子类必须在 IoC 中注册(对于类的拦截器,类都必须要注册,当然,拦截器也必须要注册)。

 

示例代码:

var builder = new ContainerBuilder();builder.RegisterType<First>()       .EnableClassInterceptors();builder.Register(c => new CallLogger(Console.Out));


基于 WCF 的拦截器

 

     WCF 是一种特殊情况。虽然 WCF Proxy 的服务对象也是一种接口,但是使用 EnableInterfaceInterceptors 不会起作用,因为 .NET 实际上是使用了  类似于接口行为的 System.Runtime.Remoting.TransparentProxy 。因此需要这里需要使用  InterceptTransparentProxy() 方法。

 

示例代码:

var cb = new ContainerBuilder();

cb.RegisterType<TestServiceInterceptor>();

cb.Register(c => CreateChannelFactory()).SingleInstance();

cb.Register(c => c.Resolve<ChannelFactory<ITestService>>().CreateChannel())

  .InterceptTransparentProxy(typeof(IClientChannel))

  .InterceptedBy(typeof(TestServiceInterceptor))

  .UseWcfSafeRelease();

实战一下

先看看基于接口的拦截器:

我们先定义一个借口,名为 ICalculater

using Autofac.Extras.DynamicProxy2;


namespace AOP.Interceptors

{

    //[Intercept(typeof(CalculaterInterceptor))]

    public interface ICalculater

    {

        int Add(int x, int y);


        int Sub(int x, int y);

    }

}

然后定义该接口的实现类 Calculater

using Autofac.Extras.DynamicProxy2;


namespace AOP.Interceptors

{

    //[Intercept(typeof(CalculaterInterceptor))]

    public class Calculater : ICalculater

    {

        public int Add(int x, int y)

        {

            return x + y;

        }


        public int Sub(int x, int y)

        {

            return x - y;

        }

    }

}

接下来,我们来定义拦截器。这里我们定义了两个连接器,通过这两个拦截器,我们将能很清晰的看到拦截器是如何工作的。

定义第一个拦截器 CalculaterInterceptor 

using System;

using Castle.DynamicProxy;


namespace AOP.Interceptors

{

    public class CalculaterInterceptor : IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            // 在下个拦截器或目前方法处理之前处理

            var args = invocation.Arguments;


            Console.WriteLine($"Before: x={args[0]}, y={args[1]}");

            Console.WriteLine($"Before: Method={invocation.Method.Name}");


            invocation.SetArgumentValue(0, 5);


            // handle

            invocation.Proceed();  // 调用下一个拦截器,直到最终的目标方法。


            // Post

            Console.WriteLine($"After: TargetType={invocation.TargetType}");

            Console.WriteLine($"After: ReturnValue={invocation.ReturnValue}");


            invocation.ReturnValue = (int)invocation.ReturnValue - 2;

        }

    }

}

定义第二个拦截器 CalculaterInterceptor2 :

using System;

using Castle.DynamicProxy;


namespace AOP.Interceptors

{

    public class CalculaterInterceptor2 : IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            var args = invocation.Arguments;


            Console.WriteLine($"Before2: x={args[0]}, y={args[1]}");

            Console.WriteLine($"Before2: Method={invocation.Method.Name}");


            invocation.Proceed();


            Console.WriteLine($"After2: TargetType={invocation.TargetType}");

            Console.WriteLine($"After2: ReturnValue={invocation.ReturnValue}");


            invocation.ReturnValue = (int)invocation.ReturnValue - 1;  // 将结果值减去 2

        }

    }

}

在 控制台 Main 函数输入我们的结果:

static void Main(string[] args)

        {

            var builder = new ContainerBuilder();

            builder.RegisterType<Calculater>()

                .As<ICalculater>()

                .EnableInterfaceInterceptors()

                .InterceptedBy(typeof(CalculaterInterceptor), typeof(CalculaterInterceptor2));  // 这里定义了两个拦截器,注意它们的顺序


            builder.RegisterType<CalculaterInterceptor>();  // 注册拦截器

            builder.RegisterType<CalculaterInterceptor2>();  // 注册拦截器2


            var ioc = builder.Build();


            var calculater = ioc.Resolve<ICalculater>();

            var addResult = calculater.Add(2, 3);


            Console.WriteLine($"add result: {addResult}");


            Console.WriteLine("-------------------");

            Console.ReadLine();

        }

我们看看输出结果:

 

这里我们可以看出,执行顺序为 CalculaterInterceptor  --> CalculaterInterceptor2 --> Target Method --> CalculaterInterceptor2 --> CalculaterInterceptor。拦截器中 invocation.Proceed() 方法用于调用下一个拦截器(若存在),直到最终的目标方法(Target Method)。不过 invocation.Proceed() 并不是一定要调用的,例如,对于有返回值的目标方法,我们在拦截器中设置 invocation.ReturnValue 值就可正确执行,这样便不会执行目标方法。在有些场景中,如身份验证、缓存读取等还是特别有用的。

当然,在 Main() 方法中 Ioc 注册 Caliculater 类型时我们注册了两个拦截器,".InterceptedBy(typeof(CalculaterInterceptor), typeof(CalculaterInterceptor2))"。我们也可以直接在 Calculater 类型 或 ICalculater 接口上以特性的形式注册,如上面代码中注释掉的那部分。若是既有在类型上注册,也有在 Autofac 的 Builder 中注册,那么这个拦截器会重复执行。

 

基于类的拦截器:

我们定义两个类 Flight 和其 子类 FlightOfSH:

public class Flight

    {

        public virtual void Fly(DateTime time)

        {

            Console.WriteLine($"Flight: {time}");

        }

    }



public class FlightOfSH : Flight

    {

        public override void Fly(DateTime time)

        {

            Console.WriteLine($"FlightOfSH: Fly={time}");

        }


        public void Arrive(DateTime time)

        {

            Console.WriteLine($"FlightOfSH: Arrive={time}");

        }

    }

这两个类的拦截器:


internal class FlightInterceptor : IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            Console.WriteLine("Before Fly");


            invocation.Proceed();


            Console.WriteLine("After Fly");

        }

    }

在 Main 函数中定义代码:


var builder = new ContainerBuilder();


            builder.RegisterType<Flight>()

               .EnableClassInterceptors().InterceptedBy(typeof(FlightInterceptor));

            builder.RegisterType<FlightOfSH>()

                .EnableClassInterceptors().InterceptedBy(typeof(FlightInterceptor));


            builder.RegisterType<FlightInterceptor>();


            var ioc = builder.Build();


            var flight = ioc.Resolve<Flight>();

            flight.Fly(DateTime.Now);


            var flightOfSH = ioc.Resolve<FlightOfSH>();

            flightOfSH.Fly(DateTime.Now);

            flightOfSH.Arrive(DateTime.Now);


我们看看输出结果:

 

 

从输出结果中可以发现一个有趣的现在, 子类 FlightOfSH 重写了 Flight 的 Fly 方法,却也调用才拦截器。Arrive() 方法因为是非虚方法,所有拦截器不会在该方法中调用。

当然,我们这里都是直接在 Autofac 的 Ioc 注册类型时设定的,也可以同上面一样使用 InterceptAttribute 特性来注入。

对于基于 WCF 的拦截器,大家可以自己试验下。

 

总结

这里简单的介绍了 Autofac 与 Castle 动态代理功能结合来实现 AOP 功能,当然,Castle 本身也是个很强大的开源框架,也有很强大的 IoC 功能,不过我还是比较喜欢 Autofac 的 IoC 功能。

相关文章:

  • 使用 Autofac 进行依赖注入

  • ASP.NET Core依赖注入解读&使用Autofac替代实现


原文链接:http://www.cnblogs.com/god--love-you/p/5699632.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

阅读全文
0 0
原创粉丝点击