Autofac全面解析系列(版本:3.5) – [使用篇(推薦篇):3.依賴注入]

前言

autofac

Autofac是一套高效的依賴注入框架。html

Autofac官方網站:http://autofac.org/git

Autofac在Github上的開源項目:https://github.com/autofac/Autofacgithub

Autofac安裝:經過VS的Nuget能夠很方便的獲取。框架

 

依賴注入

依賴注入,這個專業詞咱們能夠分爲兩個部分來理解:學習

依賴,也就是UML中描述事物之間關係的依賴關係,依賴關係描述了事物A在某些狀況下會使用到事物B,事物B的變化會影響到事物A;網站

注入,醫生經過針頭將藥物注入到病人體內。注入也就是由外向內注入、灌輸一些東西。ui

綜合上面的說明,依賴注入就是A類依賴B類,B類的實例由外部向A注入,而不是由A本身進行實例化或初始化。this

 

三種注入方式

構造器注入

咱們先理解構造器注入的字面意思,構造器注入也就表示,依賴關係經過構造器進行注入。spa

這種咱們平時是很是常見的,類A依賴於類B,類A的構造方法中,有一個參數爲類B,在new 類A,會從外部爲類B傳入實例,這就是構造注入:code

class Program
{
    static void Main(string[] args)
    {
        var b = new B();
        var a = new A(b);
    }
}

class A
{
    private B _b;

    public A(B b)
    {
        this._b = b;
    }
}

class B { }

上面說明了構造注入的含義以及構造注入的表現形式,下面咱們來看看autofac中的構造注入。

還記得在上一篇博文Autofac全面解析系列(版本:3.5) – [使用篇(推薦篇):2.解析獲取] 中的Resolve對象構造方法選擇原則小結中提到了構造注入這個概念。在使用autofac時,構造注入是默認行爲。

以上面的代碼爲例,若是類型A和類型B都註冊到了autofac中,那麼在經過autofac解析獲取A時,autofac會檢測到A的構造方法中是要一個參數B,而類型B是已經註冊到autofac中的,因此autofac會自動建立b參數,而後傳入A的構造方法中的。這樣,autofac就自動幫咱們完成了構造注入的工做。

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<A>();
        builder.RegisterType<B>();

        var container = builder.Build();
        var a = container.Resolve<A>(); //A的構造方法須要參數b,可是這裏不須要作更多地操做
    }
}
class A
{
    private B _b;

    public A(B b)
    {
        this._b = b;
    }
}

class B { }

 

屬性注入

屬性注入也就是經過屬性進行注入,咱們修改上面的A類,將變量_b經過屬性暴露出來,而且刪掉有參構造方法,而後讓咱們看看咱們日常寫代碼時怎麼實現屬性注入的:

class Program
{
    static void Main(string[] args)
    {
        var a = new A();    //點擊A查看A類修改後結構
        var b = new B();
        a.B = b;    //經過屬性來注入具備依賴關係的B
    }
}
class A
{
    private B _b;

    public B B
    {
        get { return _b; }
        set { _b = value; }
    }
}

class B { }

這種代碼在平常中咱們寫過了無數遍,即便是這麼日常的代碼,但這就是屬性注入。

依賴注入注意點

可是有一點仍是要注意的,咱們不能隨便把這種相似的代碼拿出去就告訴別人,咱們須要注意一點,須要分清二者之間是否真的是依賴關係。好比領域模型,簡單的領域模型就是將數據表映射爲一個類,對於數據表的每一個字段,咱們會生成一個對應的屬性,對於這種,咱們不可以在爲每一個屬性進行賦值時就說「這是依賴注入」,這並非依賴注入,更多狀況下,字段與表的關係是一個組合關係。這一點對於以前的構造注入和後面會講到的方法注入都適用。

說完注意點,讓咱們再來看看autofac是怎麼進行屬性注入的:

自動屬性注入

屬性注入的全部注入方式都是在註冊時定義的,不像構造注入那般,能夠在Resolve時傳參注入。

構造器注入是默認行爲,不須要設置,默認會去檢查,而屬性注入並非默認行爲。可是咱們能夠經過設置,讓屬性注入也成爲自動注入。

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        // 經過PropertiesAutowired制定類型A在獲取時會自動注入A的屬性
        builder.RegisterType<A>().PropertiesAutowired();
        builder.RegisterType<B>();

        var container = builder.Build();
        var a = container.Resolve<A>();

        Console.Write("Press any key to continue...");
        Console.ReadKey();
    }
}

使用PropertiesAutowired也只是能指定某個類會自動進行屬性注入,沒有一鍵設置全部類型都會自動注入屬性的設置。並且還須要注意一點,設置了自動屬性注入後,也不表明全部屬性都會自動注入,只有註冊到Autofac中的類型才能自動注入。

WithProperty、WithProperties

PropertiesAutowired方式會自動注入全部能夠注入的屬性,可是若是隻想注入指定幾個屬性,可使用除PropertiesAutowired之外的幾種注入方式,WithProperty就是其中一種:

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<A>().WithProperty(new NamedPropertyParameter("B", new B()));
        // builder.RegisterType<A>().WithProperty("B", new B());    //效果與上面相同
var container = builder.Build(); var a = container.Resolve<A>(); Console.Write("Press any key to continue..."); Console.ReadKey(); } }

用法簡單,WithProprtties的使用方式與WithProperty類似,在此就不貼代碼了。

lambda

在註冊篇裏面有講到一種lambda註冊方式,lambda註冊時,由於是寫lambda表達式進行註冊,其lambda內容能夠寫不少,其中就能夠進行屬性注入:

var builder = new ContainerBuilder();

builder.Register(c =>
{
    var _a = new A();
    _a.B = new B(); //手動注入
    return _a;
});

這裏的注入,就是最開始講到屬性注入時的那種賦值注入。

事件

在autofac中,還有一些事件,這些事件在不一樣時期觸發,事件相關的具體內容將在後續說明。在注入中可以使用到的事件有OnActivating和OnActivated,他們是在對象Resolve出來後觸發,能夠在事件中修改或替換返回對象,一樣也能夠進行屬性注入:

var builder = new ContainerBuilder();

builder.RegisterType<A>().OnActivating(e =>
{
    e.Instance.B = new B(); //Instance爲Resolve出來的實例,類型爲A
});

OnActivated事件的寫法與OnActivating相同,關於兩個事件的區別,將在後續博文中進行說明,請持續關注!

 

方法注入

方法注入也不是默認行爲,並且尚未提供像屬性注入那樣的自動注入設置。

方法注入有兩種方式,也就是屬性注入的後兩種方式:lambda以及事件。你們應該已經可以想到注入的代碼是什麼樣了:

var builder = new ContainerBuilder();

// lambda
builder.Register(cc =>
{
    var _a = new A();
    _a.MethodInjection(new B());
    return _a;
});

// 事件
builder.RegisterType<A>().OnActivated(e =>
{
    e.Instance.MethodInjection(new B());
});

MethodInjection爲A的一個方法,而且它須要一個類型爲B的參數,咱們在外部經過方法的方式將B傳入,這就是方法注入。這裏須要特別貼一下A類型的代碼,相對以前有所改動,不只僅是添加了一個方法:

class A
{
    public void MethodInjection(B b)
    {
        // 作一些操做
    }
}

這段代碼可能與一些朋友想象中的不同,有些朋友可能想着A中還有一個成員_b,而後在方法MethodInjection中將b賦值給_b。這裏我特地將成員_b去掉,爲的就是說明一個問題:

不是兩個類型之間必定是成員關係,而後纔能有依賴注入,咱們得理解依賴的含義。A類型在某些操做中須要使用到B類型,而並不將B類型持久的保存起來,臨時使用也是一種依賴關係。關於爲何這樣就算做依賴注入,在咱們瞭解剛剛說的依賴關係後,再來看看依賴的注入與不注入的不一樣形式,若是不是注入的方式,那麼B類型將不作爲參數傳入,而直接在方法中new。

而依賴注入的好處,在這裏還不能很好的看到,由於如今是在講autofac中關於注入的方式。若是想更只管的看到注入的好處,咱們將參數B換成接口IClass,使用注入的方式,咱們在外部傳入IClass的實例,由於IClass是接口,咱們能夠傳入不一樣的實現,在更換實現時,這個方法內部的代碼是不須要改動的,反之是須要改動的。依賴注入的好處,咱們點到爲止了,主要仍是要在平常多使用對比,這樣才能更切身的體會它的美妙之處!

 

尾述

疑問

關於注入這塊兒,其實我我的有個疑問,關於autofac。屬性注入中,咱們能夠經過設置PropertiesAutowired進行自動注入,可是有時,可能大部分屬性咱們都但願可以自動注入,然而有時會有那麼幾個屬性咱們須要自動注入忽略掉他們,在我想來,應該是有一個Attribute用於標記屬性,被標記的屬性會在屬性自動注入時被忽略。

而我想的這種Attribute,我找了找,autofac中貌似並沒看到。也多是我本身忽略掉了,若是你們誰有知道的,煩請指導一下,謝謝Smile

 

尾述

我的仍是推薦使用默認最簡單的構造注入,不須要傳參的那種;屬性注入推薦設置自動屬性注入,若是可以找到疑問中說到的那種Attribute,那就更好了;方法注入仍是不怎麼推薦的。

其實這裏的推薦原則是這樣的,須要在註冊時進行指定注入的方式實際是不太好的,由於後來的人可能不太清楚每一個類型的注入規則,還須要到註冊的地方進行查看,並且不一樣人員寫的不一樣,這樣容易混亂。而在獲取時進行注入,實際也是不太妥的,由於在實際的用法中,咱們會將註冊類型與接口進行關聯,在獲取時直接獲取接口類型。也正由於咱們獲取時獲取的是接口類型,咱們沒法保證接口的實際實現是否是具備咱們預期的參數。

若是有任何問題,還但願你們可以提出討論,互相學習。也但願可以有前輩對博客的內容及表達方式提出意見和建議,謝謝!

相關文章
相關標籤/搜索