2020年8月30日星期日

把Autofac玩的和javaSpring一样6

spring框架在.net平台实践

大家好,今天来介绍我开源的一个autofac.Annotation项目 源码:https://github.com/yuzd/Autofac.Annotation

  • 本项目是autofa的一个扩展组件,autofac是一个老牌的DI容器框架 ,支持netframework和netcore
  • Annotdation是注解的意思,在java项目里面 注解的概念和 csharp里面的 Attribute 的概念是一样的。

本项目的目的 降低玩DI容器的门槛,快速实现依赖注入 自动装配 以及拦截器,AOP切面编程

基于参考 Java的 Spring注解方式开发思想,

所有autofac容器的注册 和 装配 都是依赖标签来完成。

这样一来 一方面很容易分清楚 哪些是DI 哪些非DI, 哪些是拦截器,哪些需要拦截器,轻松实现切面编程, 代码也好看,吸收java的spring框架的优越的地方,配合.net语法的优越性,编程效率能够大大提升。

支持的标签一览

标签名称使用位置使用说明
AutoConfiguration打在class上面自动装配class里面带有Bean标签的方法
Bean打在方法上面配合AutoConfiguration标签使用
Component打在class上面自动注册
Autowired打在构造方法的Parameter,类的Property,类的Field自动装配
PropertySource打在class上面配合Value标签使用,设置Value的数据源,支持json,
Value打在构造方法的Parameter,类的Property,类的Field静态数据装配,支持强大的EL表达式
Aspect打在class上面开启拦截器,默认注册为类拦截器,也可以指定为接口型拦截器(和pointcut的区别是它是打在哪个class哪个才会生效)
Pointcut打在class上面切面配置,一个切面拦截N多个对象

针对每个标签的使用文档 请移步wiki文档传送门

https://github.com/yuzd/Autofac.Annotation/wiki

 

下面介绍一下最最常用的功能也就是注册和自动装配

 

Componet标签把类型注册到DI容器

原理解释

框架会扫描打了Componet标签的class。如果Componet标签里面指定了要注册的类型,则会只注册为这个类型到DI容器。如果没有指定则会把当前class(参考下面的4),以及父类,以及接口都会注册到DI容器(参考下面的1,2和3)。

如何修改这个默认配置呢,比如关闭自动注册父类和接口。 可以参考 https://github.com/yuzd/Autofac.Annotation/issues/11

Componet有哪些属性

属性名称类型含义
ServiceType注册指定单个的类型
KeyString注册指定单个的key(为了同个类型注册多次避免歧义)
ServicesType[]注册指定多个的类型
KeysString[]注册指定多个的key(如果指定多个类型又想避免歧义可以搭配上面一起使用)
AutofacScopeEnum

InstancePerDependency(每次都是一个新实例,默认) SingleInstance(单例)

InstancePerLifetimeScope(每个scope获取新的实例) InstancePerRequest(根据每个请求一个实例)

可以在插件初始化的时候指定一个默认的 参考下方的截图说明

AutoActivatebool当指定Scope为单例的时候 默认false 当DI容器初始化完成后会自动完成实例化
InitMethodstring当实例化后自动执行的方法名称
DestroyMethodstring当实例会DI容器回收会自动执行的方法名称
OwnershipEnumLifetimeScope(DI容器管理自动回收策略,默认)External(自己手动管理实例回收)
InterceptorType指定拦截器类型
InterceptorTypeEnumInterface(使用接口模式) Class(使用class的虚方法模式)
InterceptorKeystring如果同一个类型的拦截器有多个 可以指定Key
InjectPropertyTypeEnum属性自动装配的类型 ,Autowired(代表打了Autowired标签的才会装配),All(代表全部自动装配)

Componet的常用构造方法

  1. 默认的构造方法
//默认的构造方法会把当前class,以及父类,以及接口都会注册到DI容器//这里只会注册A8到DI容器[Component]public class A8{ }//通过A8类型可以装配成功[Autowired]public A8 A8 { get; set; }·························································public class B{}//这里会把A8 和 B 都注册到DI容器[Component]public class A8:B{ }//通过A8类型可以装配成功[Autowired]public A8 A8 { get; set; }//通过B类型也可以装配成功 拿到的是A8类型[Autowired]public B B { get; set; }··························································public interface IA{ }public interface IB:IA{ }public class B:IB{}//这里会把A8 B IB IA 全都注册到DI容器 //如果你指向注册A8 可以参考https://github.com/yuzd/Autofac.Annotation/issues/11 去配置[Component]public class A8:B{ }//通过A8类型可以装配成功 [Autowired]public A8 A8 { get; set; }//通过B类型也可以装配成功 拿到的是A8类型[Autowired]public B B { get; set; }//通过IB类型也可以装配成功 拿到的是A8类型[Autowired]public IB IB { get; set; }//通过IA类型也可以装配成功 拿到的是A8类型[Autowired]public IA IA { get; set; }
  1. 注册为指定类型
public class B{}//这个构造方法就是将A6注册为B类型 [Component(typeof(B))]public class A6:B{}//可以通过下面的方式自动装配 因为上面注册的是B类型 通过B类型可以装配成功 拿到的是A6类型[Autowired]public B B { get; set; }//通过下面的方式自动装配会失败 会失败 会失败[Autowired]public A6 A6 { get; set; }
  1. 同一个注册类型有多个 采用Key方式解决歧义
public interface ITestAutowiredModal{}// ITestAutowiredModal这个类型被注册多个了 避免歧义用"abc"来解决[Component("abc")]public class TestAutowiredModal1:ITestAutowiredModal{}// ITestAutowiredModal这个类型被注册多个了 避免歧义用"def"来解决[Component("def")]public class TestAutowiredModal2:ITestAutowiredModal{}//可以用下面的方式来自动装配 拿到的是TestAutowiredModal1类型对象[Autowired("abc")] public ITestAutowiredModal TestAutowiredModal1 { get; set; }//如果不指定的话会先尝试根据byType模式匹配,因为是指定了Key 所以根据byType拿不到,拿不到就会再次根据属性名称 “abc” 去匹配,就和上面的方式一样了 [Autowired] public ITestAutowiredModal abc { get; set; }//可以用下面的方式来自动装配 拿到的是TestAutowiredModal2类型对象[Autowired("def")] public ITestAutowiredModal TestAutowiredModal2 { get; set; } 

1. 把一个类型注册到DI容器

image

  • 上面的例子就是把Student类型注册到容器

2. 把当前类型和父类注册到DI容器

image

  • 上面的例子就是把Student2类型注册到容器
  • 并且把Person类型也注册到容器根据Person类型拿到的是Student2的实体

3. 把当前类型和接口注册到DI容器

image

  • 上面的例子就是把Student3类型注册到容器
  • 并且把ISay也注册到容器根据ISay类型拿到的是Student3的实体

4. 当继承了父类或者接口 想要指定注册类型怎么办?

image

  • 上面的例子就是只能通过ISay拿到Student3的实体
  • 不能通过Student3类型拿到!!

5. 怎么指定实例是单例,每次都是新的实例,还是每个Scope一个实例呢?

image

  • 如上图所示 可以指定AutofacScope属性
  • 如果不指定就是每次获取的一个新的实例

6. 当同一个类型多次注册,如何区分得到我想要的?

image

  • 如上图 ISay类型有2个实现类 Student3 和 Student4 分别指定了 Key的值
  • 通过ISay + “Student3” 可以获取到 Student3的实体
  • 通过ISay + “Student4” 可以获取到 Student4的实体

7.支持注册类型可以自动实例化对象

image

  • 如上所示,设定AutoActivate = true代表是启动自动实例化
  • AutofacScope = AutofacScope.SingleInstance 代表单例模式
  • Student5类型会对象会自动实例化 并且以单例的方式存储在DI容器内

8.支持设置实例化时运行指定方法

image

  • 如上所示 设置 InitMethod 和 DestroyMethod
  • 当实例从DI容器初始化时就会调用 InitMethod
  • 当DI容器Dispose的时候会触发调用DestroyMethod

另外

image

  • InitMethod支持注入
  • DestroyMethod只能是无参数方法

Autowired自动装配

Autowired有哪些属性

属性名称类型含义
NameString搭配Component指定Key的时候使用,消除一个类型被多个注册带来的歧义
Requiredbool默认装载失败会报错 设置为false装载失败不会报错
CircularDependenciesbool是否支持循环注入 默认是false 不支持,插件初始化的时候可以设置一个默认值

 

打在构造方法上 属性

image

在单例的对象里面Autowired多实例

使用ObjectFactory来实现,和Lazy的区别是 ObjectFactory修饰的每次获取都是从容器里面获取一遍。而Lazy只有首次才会去容器获取。

image

延迟自动装配Lazy

[Component]public class TestLazyModel1{ [Autowired] public Lazy<TestAutowiredModal4> TestAutowiredModal4 { get; set; }  [Autowired] public TestAutowiredModal3 TestAutowiredModal3 { get; set; }}
把Autofac玩的和javaSpring一样6新手专区美国Newegg招商沙龙交流会贝恩资本美国宣布对华进口商品不提高关税税率 !亚马逊如何从零打造爆款?产品莫名遭遇亚马逊"危险品审查"?莫慌...包裹量激增日本创新部署"最后一英里"服务Buy Box黄金购物车