Autofac實現有條件的DI

俞正東發表於2022-01-08

Autofac.Annotation框架是我用.netcore寫的一個DI框架,基於Autofac參考 Spring註解方式所有容器的註冊和裝配,切面,攔截器等都是依賴標籤來完成。

開源地址:https://github.com/yuzd/Autofac.Annotation

本期講的是最新實現的功能

有條件的DI

有些時候我們想要滿足xxx條件才把一個類註冊到容器裡面。比如如何切換Services,如果是Spring,可以根據條件註冊Bean和Configuration。所以我參考Spring的條件註解也在我的Autofac.Annotation框架中也實現了以下註解:

註解 使用方式 備註
Conditional 打在class或者方法上面 條件載入,自定義實現的
ConditionOnBean 打在標有Bean註解的方法上面 條件載入
ConditionOnMissingBean 打在標有Bean註解的方法上面 條件載入
ConditionOnClass 打在class或者方法上面 條件載入
ConditionOnMissingClass 打在class或者方法上面 條件載入
ConditionOnProperty 打在class或者方法上面 條件載入
ConditionOnProperties 打在class或者方法上面 條件載入
DependsOn 可以配合Bean和Component使用 A的例項化依賴另一個B的例項化,但是A並不需要持有一個B的物件

image

下面來講講使用方法:

ConditionOnBean和ConditionOnMissingBean

這2個註解是隻能配合Bean註解一起使用,且只能打在方法上面,不能打在class上面

  1. ConditionOnBean的意思是,如果指定的類已經被註冊的話,我才要註冊。

[AutoConfiguration]
public class Test10Config
{
    [Bean]
    [ConditionOnBean(typeof(Test10Model3))]
    public Test10Model5 getTest10Model5()
    {
        Console.WriteLine("registered Test10Model5");
        return new Test10Model5();
    }
    
}

上面的程式碼的意思是,如果Test10Model3被註冊的話,才會註冊Test10Model5

  1. ConditionOnMissingBean的意思是,如果指定的類沒被註冊的話,我才要註冊。

[AutoConfiguration]
public class Test10Config
{
    [Bean]
    [ConditionOnMissingBean(typeof(Test10Model1))]
    public Test10Model3 getTest10Model3()
    {
        Console.WriteLine("registered Test10Model3");
        return new Test10Model3();
    }
    
}

上面的程式碼的意思是,如果Test10Model1沒被註冊的話,才會註冊Test10Model3

ConditionOnClass和ConditionOnMissingClass

這2個註解是配合Compoment或者AutoConfiguration,PointCut等註解一起使用,可以打在class和method上面,該註解的引數需要填入類的完整名稱

  1. ConditionOnClass的意思是如果當前執行環境存在指定的類的話,就註冊

[Bean]
[ConditionOnClass("Autofac.Annotation.Test.Test10Model2,Autofac.Configuration.Test")]
public Test10Model6 getTest10Model6()
{
    //找的到class 所以可以註冊Test10Model6
    Console.WriteLine("registered Test10Model6");
    return new Test10Model6();
}

  1. ConditionOnMissingClass的意思是如果當前執行環境不存在指定的類的話,就註冊

[Bean]
[ConditionOnMissingClass("Autofac.Annotation.Test.test10.Test10Model2,xxxx")]
public Test10Model7 getTest10Model7()
{
    //找不到class 所以註冊Test10Model7
    Console.WriteLine("registered Test10Model7");
    return new Test10Model7();
}

ConditionOnProperty和ConditionOnProperties

這2個註解可以配合Bean,Compoment,AutoConfiguration,PointCut等註解一起使用,可以打在class和method上面

意思是,如果資料來源(讀取當前專案的appsettings.json)

  • 指定的key對應的值為xxx時
  • 或者不存在指定的key

就註冊

appsettings.json


{
  "onproperty": "on"
}
  1. 裡面存在指定的key為xxx時就註冊

[Bean]
[ConditionalOnProperty("onproperty", "on")]
public Test10Model8 getTest10Model8()
{
    //因為配置檔案onproperty的值為on 所以會註冊
    Console.WriteLine("registered Test10Model8");
    return new Test10Model8();
}

  1. 或者不存在指定的key

[Bean]
[ConditionalOnProperty("onproperty1", matchIfMissing = true)]
public Test10Model10 getTest10Model10()
{
    //由於配置檔案裡面沒有onproperty1 所以會註冊
    Console.WriteLine("registered Test10Model10");
    return new Test10Model10();
}

當想要指定多個值同時滿足的話就用ConditionOnProperties,道理是一樣的~!

Conditional

這個註解接受一個實現了ICondition介面的Type型別的引數。具體的判斷條件由自己實現(比如上面的幾個條件註解都滿足不了你,那你就用這個註解搭配自定義的條件)

首先我們定義一個class實現ICondition介面的ShouldSkip方法,下面的類的意思看註釋應該可以明白:

public class Test10Condition : ICondition
{
    /// <summary>
    /// 只有當 windows 系統下才被註冊
    /// </summary>
    /// <param name="context"></param>
    /// <param name="metadata"></param>
    /// <returns>返回true代表不滿足條件,那就不會被註冊到容器</returns>
    public bool ShouldSkip(IComponentRegistryBuilder context, object metadata)
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            //是linux系統 就不註冊
            return true;
        }

        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            //是mac系統 也不註冊
            return true;
        }
        //是windows系統 那就註冊
        return false;
    }
}


下面我們來使用上面的條件用Conditional註解打在方法上面,這個條件表明了只有在windows平臺才會將Test10Model1註冊到容器中


[AutoConfiguration]
public class Test10Config
{
    [Bean]
    [Conditional(typeof(Test10Condition))]
    public Test10Model1 getTest10Model1()
    {
        Console.WriteLine("registered Test10Model1");
        return new Test10Model1();
    }
    
}

上面的例子是結合Bean註解一起使用,Conditional註解還可以打在class上面,結合Compoment或者AutoConfiguration註解來實現滿足條件才註冊!

Conditional也是上面幾個其他註解的父類

image
image

不同的是上面幾個其他註解的構造方法都指定了自己預設的實現類。

這樣面向設計的好處是在註冊的初始化階段針對驗證條件的邏輯就可以統一處理:只蒐集 Conditional或者Conditional的子類註解,且約束了條件判斷的類統一得實現ICondition介面。


image

DependsOn

該註解可以配合Bean和Component註解一起使用,是用來表示一個 A的例項化依賴另一個B的例項化, 但是A並不需要持有一個B的物件


[Bean]
[DependsOn(typeof(Test12Bean4))]
public Test12Bean3 get13()
{
    Debug.WriteLine("new Test12Bean3");
    return new Test12Bean3 { Hello = "world" };
}

[Bean]
public Test12Bean4 get14()
{
    Debug.WriteLine("new Test12Bean4");
    result.Add("get14");
    return new Test12Bean4 { Hello = "world" };
}

上面的意思是在需要載入Test12Bean3例項(還沒)的時候,由於設定了DependsOn類Test12Bean4,先去載入Test12Bean4


[Component]
[DependsOn(typeof(Test12Bean8))]
public class Test12Bean7
{
    public Test12Bean7()
    {
        //Console.WriteLine("然後我在載入") 
    }
    public string Hello { get; set; }
}


[Component]
public class Test12Bean8
{
    public Test12Bean8()
    {
        //Console.WriteLine("我先載入")
    }
    public string Hello { get; set; }
}

上面的意思是在需要載入Test12Bean7的例項的時候,先去載入Test12Bean8

好了,有條件的DI介紹到此,更多教程請參考專案wiki(教程很詳細哦,別忘記給個star)

https://github.com/yuzd/Autofac.Annotation/wiki

 

關注公眾號一起學習

Autofac實現有條件的DI

相關文章