Autofac全面解析系列(版本:3.5) – [使用篇(推薦篇):5.生命周期事件]


前言

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第一個不同點。關於第二個不同點,現在暫時只發現這個區別與自動屬性注入有關,因為自動屬性注入實際上是作為一個委托注冊到OnActivatingOnActived事件中的,為什么是或呢?因為根據調用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事件,個人還沒有找到具體差別,后續還需要從底層代碼來看差別,暫時沒有看出,有知道的朋友,煩請告知下,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM