SpringBoot的註解注入功能移植到.Net平台(開源)

  • 2019 年 10 月 3 日
  • 筆記

 

最近在公司用java和kotlin寫介面, 發現SpringBoot的註解來配置DI容器的功能非常的好用: 找了一下發現沒有一個net的框架實現了,所以我決定自己寫一個!

這個是我基於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有兩種構造方法

  1. 可以傳一個Assebly列表 (這種方式會註冊傳入的Assebly裡面打了標籤的類)
  2. 可以傳一個AsseblyName列表 (這種方式是先會根據AsseblyName查找Assebly 然後在註冊)

Component標籤

說明:只能打在class上面(且不能是抽象class) 把某個類註冊到autofac容器 例如:

  1. 無構造方法的方式 等同於 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; }  }  
  1. 指定Scope [需要指定AutofacScope屬性 如果不指定為則默認為AutofacScope.InstancePerDependency]
    [Component(AutofacScope = AutofacScope.SingleInstance)]      public class A      {          public string Name { get; set; }      }  
  1. 指定類型註冊 等同於 builder.RegisterType<A6>().As()
    public class B      {        }        [Component(typeof(B))]      public class A6:B      {        }  
  1. 指定名字註冊 等同於 builder.RegisterType<A6>().Keyed<A4>(“a4”)
    [Component("a4")]      public class A4      {          public string School { get; set; } = "測試2";      }  
  1. 其他屬性說明
  • OrderIndex 註冊順序 【順序值越大越早註冊到容器,但是一個類型多次註冊那麼裝配的時候會拿OrderIndex最小的值(因為autofac的規則會覆蓋)】
  • InjectProperties 是否默認裝配屬性 【默認為true】
    [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格式 支援內嵌資源
  1. 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; }      }  
  1. 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; }      }  
  1. 不指定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