天天看點

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

最近在公司用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有兩種構造方法

  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】
  • InjectPropertyType 屬性自動裝配的類型
  • Autowired 【預設值】代表打了Autowired标簽的才會自動裝配
    1. ALL 代表會裝配所有 等同于 builder.RegisterType().PropertiesAutowired()
  • AutoActivate 【預設為false】 如果為true代表autofac build完成後會自動建立 具體請參考 autofac官方文檔
  • Ownership 【預設為空】 具體請參考 autofac官方文檔
  • Interceptor 【預設為空】指定攔截器的Type
  • InterceptorType 攔截器類型 攔截器必須實作 Castle.DynamicProxy的 IInterceptor 接口, 有以下兩種
    1. Interface 【預設值】代表是接口型
    2. 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格式 支援内嵌資源
  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


           
AutofacAnnotation 35.36 us 0.1504 us 0.1407 us

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連接配接,謝謝。

繼續閱讀