Autofac 之 基于 Castle DynamicProxy2 的 Interceptor 功能

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);
    }
}
View Code

然后定义该接口的实现类 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;
        }
    }
}
View Code

 

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

定义第一个拦截器 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;
        }
    }
}
View Code

定义第二个拦截器 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
        }
    }
}
View Code

在 控制台 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();
        }
View Code

 

我们看看输出结果:

 

这里我们可以看出,执行顺序为 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}");
        }
    }
View Code

这两个类的拦截器:

internal class FlightInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine("Before Fly");

            invocation.Proceed();

            Console.WriteLine("After Fly");
        }
    }
View Code

 

在 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);
View Code

 

我们看看输出结果:
 
 
从输出结果中可以发现一个有趣的现在, 子类 FlightOfSH 重写了 Flight 的 Fly 方法,却也调用才拦截器。Arrive() 方法因为是非虚方法,所有拦截器不会在该方法中调用。
当然,我们这里都是直接在 Autofac 的 Ioc 注册类型时设定的,也可以同上面一样使用 InterceptAttribute 特性来注入。
对于基于 WCF 的拦截器,大家可以自己试验下。
 

总结

这里简单的介绍了 Autofac 与 Castle 动态代理功能结合来实现 AOP 功能,当然,Castle 本身也是个很强大的开源框架,也有很强大的 IoC 功能,不过我还是比较喜欢 Autofac 的 IoC 功能。
本文就写到此,谢谢大家。
 
参考文章:
posted @ 2016-07-23 22:08  这是个问题  阅读(4254)  评论(3编辑  收藏  举报