把Autofac玩的和javaSpring一样6

大家好,今天来介绍我开源的一个autofac.Annotation项目 源码://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,xml,支持资源内嵌
Value 打在构造方法的Parameter,类的Property,类的Field 静态数据装配,支持强大的EL表达式
Aspect 打在class上面 开启拦截器,默认注册为类拦截器,也可以指定为接口型拦截器(和pointcut的区别是它是打在哪个class哪个才会生效)
Pointcut 打在class上面 切面配置,一个切面拦截N多个对象

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

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

 

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

 

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

原理解释

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

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

Componet有哪些属性

属性名称 类型 含义
Service Type 注册指定单个的类型
Key String 注册指定单个的key(为了同个类型注册多次避免歧义)
Services Type[] 注册指定多个的类型
Keys String[] 注册指定多个的key(如果指定多个类型又想避免歧义可以搭配上面一起使用)
AutofacScope Enum

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

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

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

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

Componet的常用构造方法

  1. 默认的构造方法

//默认的构造方法会把当前class,以及父类,以及接口都会注册到DI容器
//这里只会注册A8DI容器
[Component]
public class A8
{
   
}

//通过A8类型可以装配成功
[Autowired]
public A8 A8 { get; set; }

·························································

public class B
{

}

//这里会把A8  B 都注册到DI容器
[Component]
public class A8B
{
   
}


//通过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 可以参考//github.com/yuzd/Autofac.Annotation/issues/11 去配置
[Component]
public class A8B
{
   
}


//通过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有哪些属性

属性名称 类型 含义
Name String 搭配Component指定Key的时候使用,消除一个类型被多个注册带来的歧义
Required bool 默认装载失败会报错 设置为false装载失败不会报错
CircularDependencies bool 是否支持循环注入 默认是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; }
}