最近在公司用 java 和 kotlin 写接口, 发现 SpringBoot 的注解来配置 DI 容器的功能非常的好用: 找了一下发现没有一个 net 的框架实现了, 所以我决定自己写一个!
开源地址: https://github.com/yuzd/Autofac.Annotation
支持 netcore2.0 + framework4.6+
NUGET 安装: Install-Package Autofac.Annotation
这个是我基于 autofac 框架的一个扩展组件, 实现了以下功能:
玩过 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 http://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 |
来源: https://www.cnblogs.com/yudongdong/p/11577975.html