思路 我發現 .NET Core WebAPi項目有一個與Springboot的不同之處,就是Springboot項目有自動裝配機制,他可以將在src下麵與啟動類在同一級包目錄下的類進行掃描註冊 而之前我瞭解到Springboot的自動裝配機制本質上也就是通過掃描對應包,然後進行通過它自身進行服務註 ...
思路
我發現 .NET Core WebAPi項目有一個與Springboot的不同之處,就是Springboot項目有自動裝配機制,他可以將在src下麵與啟動類在同一級包目錄下的類進行掃描註冊
而之前我瞭解到Springboot的自動裝配機制本質上也就是通過掃描對應包,然後進行通過它自身進行服務註冊,我雖然寫不出那麼牛掰的東西,但是我也打算大致仿照一下
步驟如下:
- 準備幾個裝飾類Server、Config等
- 掃描當前程式集以及引用程式集被改裝飾(註解)描述了的類和介面
- 這裡要註意註冊的順序,註冊的順序如果出錯,就會爆出異常(本人已經經歷過)
- 同時還需要註意,我們讀取配置註冊配置類必須是在最前的,因為配置載入最好是在程式啟動前全部載入完(不過想想也有懶載入的情況,這裡我沒有難麽多考慮)
代碼實現
我這裡載入文件配置的代碼和註冊類的代碼寫到了一起,有點耦合,大家看可用適當的做拆分,OK希望我的這個小工具類可用幫助到大家,如果大家有更好的想法,希望寫出來
using gobangBack.Annotation;
using System;
using System.Reflection;
namespace gobangBack.Utils
{
// 這個擴展方法僅僅對於本項目
/// <summary>
/// 步驟:
/// 1.對於當前項目的Service目錄下的所有帶有Service結尾的類型進行父子關係判斷
/// 2.進行服務類型和實現類型的一次性註入
/// </summary>
public static class ServiceAutoDI
{
public static readonly Queue<Type> RegisterQueue = new Queue<Type>();
public static void AutoAllDI(this IServiceCollection service) {
AutoServerDIFromService(service);
}
// 1.自動註冊服務類
public static void AutoServerDIFromService(this IServiceCollection service)
{
// 1.獲取當前程式集中有被Server修飾過的所有的類(並且不能是抽象類)
var classTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract)
.Where(type => Attribute.IsDefined(type, typeof(Server)))
.ToList();
// 2.獲得當前程式集中被Server修飾過的所有介面
var interfaceTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsInterface)
.Where(type => Attribute.IsDefined(type, typeof(Server)))
.ToList();
// 3.獲取當前程式集中被Config修飾過的配置類
var configClassTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract)
.Where(type => Attribute.IsDefined(type, typeof(Config)))
.ToList();
// 3.判斷類型的構造方法中是否有被Server修飾的介面或者是被Config修飾的類
// 3.2獲取到所有Server修飾類的構造函數中的所有參數
var quoteInterface = classTypes
.SelectMany(classType => classType.GetConstructors())
.SelectMany(constructor => constructor.GetParameters())
// 4.2 判斷參數中是否有interfaceTypes的值,如果有,就將其找出放入註入隊列中
.Where(param => interfaceTypes.Contains(param.ParameterType))
.Select(param => param.ParameterType).ToList();
foreach (var interfaceType in quoteInterface)
{
RegisterQueue.Enqueue(interfaceType);
interfaceTypes.Remove(interfaceType);
}
// 5.進行配置讀取
// 5.1.先載入好配置文件
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
// 5.2.進行配置文件的讀取,配置的名稱必須和類名一致
configClassTypes.ForEach(config =>
{
// 1.獲取到配置類結尾的部分
string configName = config.FullName.Substring(config.FullName.LastIndexOf(".")+1);
// 2.將配置類的配置添加到容器中
// 2.1.獲取到OptionsConfigurationServiceCollectionExtensions類中名稱為Configure參數為IServiceCollection,IConfiguration的方法
// 2.2.講它的泛型類型設置config類型
MethodInfo configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions)
.GetMethod("Configure", new Type[] { typeof(IServiceCollection), typeof(IConfiguration) })
.MakeGenericMethod(config);
// 3.讀取配置文件中的名稱為對應名稱的部分,進行載入
configureMethod.Invoke(null, new object[] { service, configuration.GetSection(configName) });
// 4.註冊配置類
service.AddScoped(config);
});
// 6.優先註冊在隊列中的介面
var prioityInterface = RegisterQueue.ToArray();
foreach(var interfaceType in prioityInterface)
{
var implenmentTypes = classTypes.Where(classType => classType.IsAssignableTo(interfaceType))
.ToList();
implenmentTypes.ForEach(type =>
{
service.AddScoped(interfaceType, type);
});
}
// 7.註冊其它的介面類型
foreach(var interfaceType in interfaceTypes)
{
var implenmentTypes = classTypes.Where(classType => classType.IsAssignableTo(interfaceType))
.ToList();
implenmentTypes.ForEach(type =>
{
service.AddScoped(interfaceType, type);
});
}
}
}
}