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);
? ? }
}
然后定義該接口的實現類?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跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的Autofac 之 基于 Castle DynamicProxy2 的 Interceptor 功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio“15”进一步
- 下一篇: ASP.NET Core MVC Tag