SpringBoot的註解注入功能移植到.Net平臺(開源)
阿新 • • 發佈:2019-09-24
最近在公司用java和kotlin寫介面, 發現SpringBoot的註解來配置DI容器的功能非常的好用: 找了一下發現沒有一個net的框架實現了,所以我決定自己寫一個!
- 開源地址:https://github.com/yuzd/Autofac.Annotation
- 支援netcore2.0 + framework4.6+
- NUGET安裝: Install-Package Autofac.Annotation
這個是我基於autofac框架的一個擴充套件元件,實現了以下功能:
- Component標籤:註冊到DI容器直接打上一個即可
- Configuration註解和Bean標籤:實現了用例項方法註冊到DI容器
- PropertySource和Value標籤:實現了注入配置檔案屬性的值到DI容器
- Autowired標籤:實現了自動裝配
玩過java的spring框架就應該看這個標籤名稱很熟悉,因為名稱是一模一樣的。 功能也是高度保持一致
var builder = new ContainerBuilder(); // 註冊autofac打標籤模式 builder.RegisterModule(new AutofacAnnotationModule(typeof(AnotationTest).Assembly)); //如果需要開啟支援迴圈注入 //builder.RegisterModule(new AutofacAnnotationModule(typeof(AnotationTest).Assembly).SetAllowCircularDependencies(true)); var container = builder.Build(); var serviceB = container.Resolve<B>();
AutofacAnnotationModule有兩種構造方法
- 可以傳一個Assebly列表 (這種方式會註冊傳入的Assebly裡面打了標籤的類)
- 可以傳一個AsseblyName列表 (這種方式是先會根據AsseblyName查詢Assebly 然後在註冊)
Component標籤
說明:只能打在class上面(且不能是抽象class) 把某個類註冊到autofac容器 例如:
- 無構造方法的方式 等同於 builder.RegisterType();
//把class A 註冊到容器 [Component] public class A { public string Name { get; set; } } //如果 A有父類或者實現了介面 也會自動註冊(排除非public的因為autofac不能註冊私有類或介面) public interface IB { } public class ParentB:IB { public string Name1 { get; set; } } //把class B 註冊到容器 並且把 B作為ParentB註冊到容器 並且把B最為IB註冊到容器 [Component] public class B:ParentB { public string Name { get; set; } }
- 指定Scope [需要指定AutofacScope屬性 如果不指定為則預設為AutofacScope.InstancePerDependency]
[Component(AutofacScope = AutofacScope.SingleInstance)]
public class A
{
public string Name { get; set; }
}
- 指定型別註冊 等同於 builder.RegisterType<A6>().As()
public class B
{
}
[Component(typeof(B))]
public class A6:B
{
}
- 指定名字註冊 等同於 builder.RegisterType<A6>().Keyed<A4>("a4")
[Component("a4")]
public class A4
{
public string School { get; set; } = "測試2";
}
- 其他屬性說明
- OrderIndex 註冊順序 【順序值越大越早註冊到容器,但是一個型別多次註冊那麼裝配的時候會拿OrderIndex最小的值(因為autofac的規則會覆蓋)】
- InjectProperties 是否預設裝配屬性 【預設為true】
- InjectPropertyType 屬性自動裝配的型別
- Autowired 【預設值】代表打了Autowired標籤的才會自動裝配
- ALL 代表會裝配所有 等同於 builder.RegisterType().PropertiesAutowired()
- AutoActivate 【預設為false】 如果為true代表autofac build完成後會自動建立 具體請參考 autofac官方文件
- Ownership 【預設為空】 具體請參考 autofac官方文件
- Interceptor 【預設為空】指定攔截器的Type
- InterceptorType 攔截器型別 攔截器必須實現 Castle.DynamicProxy的 IInterceptor 介面, 有以下兩種
- Interface 【預設值】代表是介面型
- Class 代表是class型別 這種的話是需要將要攔截的方法標virtual
- InterceptorKey 如果同一個型別的攔截器有多個 可以指定Key
- InitMethod 當例項被建立後執行的方法名稱 類似Spring的init-method 可以是有引數(只能1個引數型別是IComponentContext)和無引數的方法
- DestroyMetnod 當例項被Release時執行的方法 類似Spring的destroy-method 必須是無引數的方法
[Component(InitMethod = "start",DestroyMetnod = "destroy")]
public class A30
{
[Value("aaaaa")]
public string Test { get; set; }
public A29 a29;
void start(IComponentContext context)
{
this.Test = "bbbb";
a29 = context.Resolve<A29>();
}
void destroy()
{
this.Test = null;
a29.Test = null;
}
}
public class B
{
}
[Component(typeof(B),"a5")]
public class A5:B
{
public string School { get; set; } = "測試a5";
public override string GetSchool()
{
return this.School;
}
}
Autowired 自動裝配
可以打在Field Property 構造方法的Parameter上面 其中Field 和 Property 支援在父類
[Component]
public class A16
{
public A16([Autowired]A21 a21)
{
Name = name;
A21 = a21;
}
[Autowired("A13")]
public B b1;
[Autowired]
public B B { get; set; }
//Required預設為true 如果裝載錯誤會拋異常出來。如果指定為false則不拋異常
[Autowired("adadada",Required = false)]
public B b1;
}
Value 和 PropertySource
- PropertySource類似Spring裡面的PropertySource 可以指定資料來源 支援 xml json格式 支援內嵌資源
- json格式的檔案
{
"a10": "aaaaaaaaa1",
"list": [ 1, 2, 3 ],
"dic": {
"name": "name1"
},
"testInitField": 1,
"testInitProperty": 1,
}
[Component]
[PropertySource("/file/appsettings1.json")]
public class A10
{
public A10([Value("#{a10}")]string school,[Value("#{list}")]List<int> list,[Value("#{dic}")]Dictionary<string,string> dic)
{
this.School = school;
this.list = list;
this.dic = dic;
}
public string School { get; set; }
public List<int> list { get; set; }
public Dictionary<string,string> dic { get; set; }
[Value("#{testInitField}")]
public int test;
[Value("#{testInitProperty}")]
public int test2 { get; set; }
//可以直接指定值
[Value("2")]
public int test3 { get; set; }
}
- xml格式的檔案
<?xml version="1.0" encoding="utf-8" ?>
<autofac>
<a11>aaaaaaaaa1</a11>
<list name="0">1</list>
<list name="1">2</list>
<list name="2">3</list>
<dic name="name">name1</dic>
</autofac>
[Component]
[PropertySource("/file/appsettings1.xml")]
public class A11
{
public A11([Value("#{a11}")]string school,[Value("#{list}")]List<int> list,[Value("#{dic}")]Dictionary<string,string> dic)
{
this.School = school;
this.list = list;
this.dic = dic;
}
public string School { get; set; }
public List<int> list { get; set; }
public Dictionary<string,string> dic { get; set; }
}
- 不指定PropertySource的話會預設從工程目錄的 appsettings.json獲取值
AutoConfiguration標籤 和 Bean標籤
[AutoConfiguration]
public class TestConfiguration
{
//Bean標籤只能搭配AutoConfiguration標籤使用,在其他地方沒有效
//並且是單例註冊
[Bean]
private ITestModel4 getTest5()
{
return new TestModel4
{
Name = "getTest5"
};
}
}
在容器build完成後執行: 掃描指定的程式集,發現如果有打了AutoConfiguration標籤的class,就會去識別有Bean標籤的方法,並執行方法將方法返回例項註冊為方法返回型別到容器! 一個程式集可以有多個AutoConfiguration標籤的class會每個都載入。
AutoConfiguration標籤的其他屬性:
- OrderIndex 可以通過OrderIndex設定優先順序,越大的越先載入。
- Key 也可以通過Key屬性設定
搭配如下程式碼可以設定過濾你想要載入的,比如你想要載入Key = “test” 的所有 AutoConfiguration標籤class //builder.RegisterModule(new AutofacAnnotationModule(typeof(AnotationTest).Assembly).SetAutofacConfigurationKey("test"));
Bean標籤的其他屬性:
- Key 也可以通過Key屬性設定 比如有多個方法返回的型別相同 可以設定Key來區分
AutofacAnnotation標籤模式和autofac寫程式碼效能測試對比
public class AutofacAutowiredResolveBenchmark
{
private IContainer _container;
[GlobalSetup]
public void Setup()
{
var builder = new ContainerBuilder();
builder.RegisterType<A13>().As<B>().WithAttributeFiltering();
builder.RegisterType<Log>().As<AsyncInterceptor>();
builder.RegisterType<Log2>().Keyed<AsyncInterceptor>("log2");
builder.RegisterType<A21>().WithAttributeFiltering().PropertiesAutowired();
builder.RegisterType<A23>().As<IA23>().WithAttributeFiltering().PropertiesAutowired().EnableInterfaceInterceptors()
.InterceptedBy(typeof(AsyncInterceptor));
builder.RegisterType<A25>().WithAttributeFiltering().PropertiesAutowired().EnableClassInterceptors()
.InterceptedBy(new KeyedService("log2", typeof(AsyncInterceptor)));
_container = builder.Build();
}
[Benchmark]
public void Autofac()
{
var a1 = _container.Resolve<A25>();
var a2= a1.A23.GetSchool();
}
}
BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362
Intel Core i7-7700K CPU 4.20GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.300
[Host] : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT [AttachedDebugger]
DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT
Method | Mean | Error | StdDev |
---|---|---|---|
Autofac | 30.30 us | 0.2253 us | 0.1997 us |
//打標籤模式
public class AutowiredResolveBenchmark
{
private IContainer _container;
[GlobalSetup]
public void Setup()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new AutofacAnnotationModule(typeof(A13).Assembly));
_container = builder.Build();
}
[Benchmark]
public void AutofacAnnotation()
{
var a1 = _container.Resolve<A25>();
var a2= a1.A23.GetSchool();
}
}
BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362
Intel Core i7-7700K CPU 4.20GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.300
[Host] : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT [AttachedDebugger]
DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT
Method | Mean | Error | StdDev |
---|---|---|---|
AutofacAnnotation | 35.36 us | 0.1504 us | 0.1407 us |