前言
autofac
Autofac是一套高效的依賴注入框架。
Autofac官方網站:http://autofac.org/
Autofac在Github上的開源項目:https://github.com/autofac/Autofac
Autofac安裝:通過VS的Nuget可以很方便的獲取。
生命周期事件
autofac為注冊的類型對象提供了一套生命周期事件,覆蓋了一個類型從注冊到最后“釋放”的一套事件。有了這些事件,我們可以相對方便的在類型對象的各個階段進行AOP操作。
五大事件
OnRegistered
在類型注冊成功后觸發,也就是在調用ContainerBuilder的Build方法時,其方法內部觸發的。OnRegistered的委托參數類型為ComponentRegisteredEventArgs,其中包含了類型注冊后的底層配置信息,此處不對配置信息做介紹,日常一般不會使用這寫參數。如果我們希望在類型注冊到autofac中后執行一些操作,我們可以通過OnRegistered事件達到目的。關於此事件,在autofac官網上沒有相關說明。
var builder = new ContainerBuilder(); builder .RegisterType<A>() //class A { } .OnRegistered(e => { Console.WriteLine(e); }); builder.Build(); //會觸發OnRegistered事件
OnPreparing
在調用Resolve時觸發,具體觸發時機,是根據Resolve的類型獲取到類型相關配置時觸發的,而這時,類型對象還沒有實例化。OnPreparing委托參數類型為PreparingEventArgs,該類型有三個屬性Component、Context、Parameter,其中Component為Resolve類型的說明/配置,Parameter為Resolve時傳入的參數(詳見解析獲取傳參)。在OnPreparing中,我們可以修改傳入的Parameter值,甚至可以以此修改實際調用的構造方法(通過Resolve對象構造方法選擇原則):
class Program { static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<A>() .OnPreparing(e => { // 1.不做任何處理時,最后輸出 result: 3 // 2.修改傳入值,最后輸出 result: 5 e.Parameters = new[] {new NamedParameter("x", 2), new NamedParameter("y", 3)}; // 3.修改參數類型,最后輸出 id: 7, name: 'undefined' e.Parameters = new Parameter[] {new PositionalParameter(0, 7), new TypedParameter(typeof (string), "undefined")}; // 4.直接不要參數,最后輸出 no parameter constructor e.Parameters = Enumerable.Empty<Parameter>(); }); var container = builder.Build(); container.Resolve<A>(new NamedParameter("x", 1), new NamedParameter("y", 2)); Console.Write("Press any key to continue..."); Console.ReadKey(); } }class A { public A() { Console.WriteLine("no parameter constructor"); } public A(int id, string name) { Console.WriteLine("id: {0}, name: '{1}'", id, name); } public A(int x, int y) { Console.WriteLine("result: {0}", x + y); } }OnPreparing事件在autofac官網上也沒有相關的說明,更多的用法待大家使用過程中進行開荒了。
OnActivating & OnActived
OnActivating與OnActived是兩個不同的事件,這里將它們放到一起講,是因為它們有一定的相似之處,起初用起來還發現不了他們之前的區別,所以放在一起進行對比說明。
相同點
與OnPreparing事件相同,OnActivating與OnActived也是在調用Resolve時觸發的,只是OnActivating與OnActived觸發是,類型實例已經創建,委托參數的類型雖然不同,但是都有且僅有四個相同類型的屬性Parameters、Component、Context、Instance,前三個參數與OnPreparing的委托參數屬性類型相同,最后一個Instance是根據Resolve傳入的類型實例化出來的實例對象,也就是最后Resolve方法返回的對象。
不同點
不同點主要有兩個,一個是它們的委托參數類型不同,雖然它們的屬性類型和個數都是相同的,但是OnActivating的委托參數還有一個ReplaceInstance方法,這個方法是用來替換最后返回的對象的,也就是相同點中說到的Instance屬性對象。但是需要注意,使用ReplaceInstance時,不是可以替換為任意類型,而是只能替換為相同類型或其子類:
var builder = new ContainerBuilder(); builder.RegisterType<C1>().As<IInterface>() .OnActivating(e => { //e.ReplaceInstance(new C2()); // 異常 //e.ReplaceInstance(new C1()); // 無異常 //e.ReplaceInstance(new CC1()); //無異常 }); var container = builder.Build(); var inter = container.Resolve<IInterface>();interface IInterface { } class C1 : IInterface { } class C2 : IInterface { } class CC1 : C1 { }ReplaceInstance方法是OnActivating與OnActived第一個不同點。關於第二個不同點,現在暫時只發現這個區別與自動屬性注入有關,因為自動屬性注入實際上是作為一個委托注冊到OnActivating或OnActived事件中的,為什么是或呢?因為根據調用PropertiesAutowired方法時,傳入的不同參數,這個委托將注冊不到不同事件上,如果不傳參或傳入PropertyWiringOptions.None,自動屬性注入,將會注冊到OnActivating事件上,否則將注冊到OnActived事件上。
關於PropertiesAutowired方法的參數,那是與環形依賴注入相關的,現在暫時不做說明,后續博文將會說明。
PropertiesAutowired注冊時機注意
因為自動屬性注入是注冊到事件上的,然后我們又知道,注冊到事件上的委托,一般是順序調用的,所以需要注意PropertiesAutowired在注冊時調用的時機。比如寫到自定義注冊OnActivating事件前,且PropertiesAutowired方法不傳入參數,那么自定義注冊的OnActivating事件觸發時,參數的Instance中自動注入的屬性將已經賦值,反之寫到自定義注冊OnActivating事件后,自動注入的屬性將為賦值!(OnActived事件同理)
var builder = new ContainerBuilder(); builder.RegisterType<C1>() //.PropertiesAutowired() // 在OnActivating前,將輸出 OnActivating: not null .OnActivating(e => { // 輸出 OnActivating: null Console.WriteLine("OnActivating: " + (e.Instance.C2 == null ? "null" : "not null")); }) .PropertiesAutowired(); // 在OnActivating后,將輸出 OnActivating: null builder.RegisterType<C2>(); var container = builder.Build(); var c1 = container.Resolve<C1>();class C1 { public C2 C2 { get; set; } } class C2 { }官網說明
OnActivating與OnActived在autofac官網中有一些說明,可以作為參考:
OnActivating事件中推薦的三種操作:1.替換實例對象,或使用代理對象(通過ReplaceInstance方法);2.執行屬性注入或方法注入;3.執行其他初始化任務。
OnActived事件中可以執行一些應用程序級別的任務。
OnRelease
在生命范圍結束時調用,關於此事件,將在后續的單元控制文章中進行詳細說明,這里暫時不做說明。
統一事件處理方式
上面說的都是為每個類型注冊事件,但是如果我們希望為所有類型都注冊某一事件,有什么方式來實現呢?
(首先申明,OnRelease事件暫時沒找到統一注冊的方式)
我們可以在builder注冊類型前使用RegisterCallback進行統一事件注冊,詳見代碼:
var builder = new ContainerBuilder(); builder.RegisterCallback(cr => { // 下面的Registered事件相當類型的OnRegistered事件 cr.Registered += (sender, eventArgs) => { // OnPreparing事件 eventArgs.ComponentRegistration.Preparing += (o, preparingEventArgs) => { }; // OnActivating事件 eventArgs.ComponentRegistration.Activating += (o, activatingEventArgs) => { }; // OnActivated事件 eventArgs.ComponentRegistration.Activated += (o, activatedEventArgs) => { }; }; }); // builder.RegisterType<...>... // ...
尾述
autofac提供的這些事件,我們可以很方便的進行AOP操作,比如通過統一事件注冊,我們可以很方便的進行日志記錄。而關於OnActivating事件和OnActived事件,個人還沒有找到具體差別,后續還需要從底層代碼來看差別,暫時沒有看出,有知道的朋友,煩請告知下,謝謝!