从容器中的可用服务中选择一个构造函数来创造对象,这个过程叫做自动装配。这个过程是通过反射实现的

默认

思考这么一个问题,如果注册类型中存在多个构造函数,那么Autofac会选择哪一个来创建类型的实例

答案是"尽可能最多参数"

  1. class ConstructorClass
  2. {
  3. private Class1 _clas1;
  4. private Class2 _clas2;
  5. private Class3 _clas3 = null;
  6.  
  7. public ConstructorClass()
  8. {
  9. _clas1 = null; _clas2 = new Class2 { Id = - };
  10. }
  11.  
  12. public ConstructorClass(Class1 clas1, Class2 clas2)
  13. {
  14. _clas1 = clas1; _clas2 = clas2;
  15. }
  16.  
  17. public ConstructorClass(Class2 clas2, Class3 clas3)
  18. {
  19. _clas2 = clas2; _clas3 = clas3;
  20. }
  21.  
  22. public ConstructorClass(Class2 clas2, Class3 clas3, Guid guid)
  23. {
  24. _clas1 = new Class1 { Id = guid }; _clas2 = clas2; _clas3 = clas3;
  25. }
  26.  
  27. public ConstructorClass(Class1 clas1, Class2 clas2, Class3 clas3)
  28. {
  29. _clas1 = clas1; _clas2 = clas2; _clas3 = clas3;
  30. }
  31.  
  32. public override string ToString()
  33. {
  34. return string.Format(
  35. "{{Class1.Id: {0}, Class2.Id: {1}, Class3: {2}}}",
  36. _clas1 == null ? "null" : _clas1.Id.ToString(),
  37. _clas2 == null ? "null" : _clas2.Id.ToString(),
  38. _clas3 == null ? "null" : "not null");
  39. }
  40. }
  41.  
  42. class Class1
  43. {
  44. public Guid Id { get; set; }
  45. }
  46.  
  47. class Class2
  48. {
  49. public int Id { get; set; }
  50. }
  51.  
  52. class Class3
  53. {
  54.  
  55. }static void Main(string[] args)
  1. static void Main(string[] args)
  2. {
  3. //注册容器
  4. var builder = new ContainerBuilder();
  5. //向容器中注册类型
  6. builder.RegisterType<ConstructorClass>();
  7. builder.RegisterType<Class2>();
  8. builder.RegisterType<Class3>();
  9. using (var container = builder.Build())
  10. {
  11. #region Resolve对象构造方法选择原则(当我们注册的类型拥有多个构造方法,那么在Resolve时,将会以尽可能最多参数构造方法为准)
  12. var obj = container.Resolve<ConstructorClass>();
  13. Console.WriteLine(obj);
  14. #endregion
  15. }
  16. Console.ReadKey();
  17. }

该实例显示,选择的是第三个构造函数,参数为(Class2 clas2, Class3 clas3),

按照字面上里说明”最多参数“,那么理应执行的是最后一个构造方法或倒数第二个构造方法,但是为什么却是第三个,这也就是为什么我要加“尽可能”三字了。

先抛开为什么执行的第三个构造方法,我们还是会有疑问”如果执行的是第三个构造方法,那么Class2和Class3参数分别赋的是什么值?值又是从哪儿来?“,这里就涉及到了后面会讲到的构造注入。我们可以看到,在进行类型注册时,我们是对Class2和Class3进行了注册的,而ConstructorClass又是通过Autofac进行获取的,所以Class2和Class3参数的值是由Autofac进行初始化赋值的,Class2和Class3没有自定义构造方法,所以调用的是默认的空构造方法。

在知道Class2和Class3参数的初始化与赋值缘由后,我们再来看看之前的那个问题,为什么会执行第三个构造方法,其实现在就好明白了,因为最后两个的构造方法,一个需要额外的Guid类型参数,另一个需要Class1类型参数,而这两个类型又没有经过注册,如果调用这两个构造方法,那么Auotofac将不知道应该赋何值给这两个参数,所以Autofac最终选择了第三个构造方法。

此时我把第三个构造函数注释掉之后,会调用第一个构造函数,按照"尽可能最多参数"原则,此时不应该调用第二个吗?答案是,Autofac会在已注册的类型中寻找,虽然Class2类型被注册,第二个构造函数Class1类型参数Autofac不知道如何赋值,所以选择了默认的构造函数,如果在容器中注册类型Class1取消掉类型Class3的注册,此时就会调用第二个构造函数.(Autofac寻找构造函数的规则是在已注册的类型中寻找参数完全匹配的构造函数)

UsingConstructor:指定使用某个构造函数

通过上面的例子我们知道Autofac创建类型实例时会默认从容器中选择匹配参数最多的构造函数,此时在容器中将Class1、Class2、Class3类型都注册,此时默认情况会使用最后一个构造函数,如果如果想要选择一个不同的构造函数,就需要在注册的时候就指定它,此时指定使用参数为(Class1 clas1, Class2 clas2)的构造函数

  1. builder.RegisterType<Class1>();
  2. builder.RegisterType<Class2>();
  3. builder.RegisterType<Class3>();
  1. builder.RegisterType<ConstructorClass>().UsingConstructor(typeof(Class1), typeof(Class2));

额外的构造函数参数

有两种方式可以添加额外的构造函数参数,在注册的时候和在检索的时候。在使用自动装配实例的时候这两种都会用到。

注册时添加参数

使用WithParameters()方法在每一次创建对象的时候将组件和参数关联起来。

  1. builder.RegisterType<ConstructorClass>().WithParameter("guid", Guid.NewGuid());
  2. //builder.RegisterType<Class1>();//将Class1注册因为在尽可能最多的原则上,出现了两个最多参数的构造方法,Autofac不知道应该选择哪个进行执行
  3. builder.RegisterType<Class2>();
  4. builder.RegisterType<Class3>();

在检索阶段添加参数
在Resolve()的时候提供的参数会覆盖所有名字相同的参数,在注册阶段提供的参数会覆盖容器中所有可能的服务。

  1. var obj = container.Resolve<ConstructorClass>(new NamedParameter("guid", Guid.NewGuid()));

自动装配

在需要的时候,依然可以创建指定的构造函数创建指定的类。

  1. builder.Register(c => new Clas1());

Resolve的方法签名为:Resolve<T>(this IComponmentContext context, params Parameter[] parameters)

第一个参数也就是我们使用的container,我们主要关注第二个参数——一个可变的Parameter数组,Parameter是一个抽象类,其中NamedParameter为Parameter的一个子类,除了NamedParameter,还有以下几种子类拱Resolve时使用:

参数类型

参数说明

NamedParameter

根据名称进行匹配

PositionalParameter

根据索引进行匹配,注意:起始索引为0

TypedParameter

根据类型进行匹配,注意:传入多个相同类型的TypedParameter,所有该类型的参数都将采用第一个TypedParameter的值

ResolvedParameter

接收两个Func参数,两个Func签名都接收两个相同的参数ParameterInfo和IComponmentContext,第一个参数为参数的信息(常使用放射的朋友应该熟悉),第二个参数还是当做IContainer使用就好了。第一个Func的返回值为bool,表明当前这个ResolvedParameter是否使用当前匹配到的参数,如果返回true,则会执行第二个Func;第二个Func返回一个object对象,用于填充构造参数值。

下面有一个这些Parameter使用的示例供参考:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var builder = new ContainerBuilder();
  6. builder.RegisterType<ParameterClass>();
  7.  
  8. var container = builder.Build();
  9. container.Resolve<ParameterClass>(
  10. new NamedParameter("value", "namedParameter"), //匹配名字为value的参数
  11. new TypedParameter(typeof (int), 1), //匹配类型为int的参数
  12. new PositionalParameter(4, "positionalParameter"), //匹配第五个参数(注意,索引位置从0开始)
  13. new TypedParameter(typeof (int), -1), //这个将被抛弃,因为前面已经有一个类型为int的TypedParameter
  14. new ResolvedParameter(
  15. //第一个Func参数用于返回参数是否符合要求,这里要求参数是类,且命名空间不是System开头,所以第四个参数将会匹配上
  16. (pi, cc) => pi.ParameterType.IsClass && !pi.ParameterType.Namespace.StartsWith("System"),
  17. //第二个Func参数在第一个Func执行结果为true时执行,用于给参数赋值,也就是第四个参数的值为这个Func的执行结果
  18. (pi, cc) => new Temp {Name = "resolveParameter"})
  19. );
  20. // 最后的输出结果为: {x:1, y:1, value:'namedParameter', temp.Name:'resolveParameter', obj:'positionalParameter'}
  21.  
  22. Console.Write("Press any key to continue...");
  23. Console.ReadKey();
  24. }
  25. }
  26.  
  27. class ParameterClass
  28. {
  29. public ParameterClass(int x, int y, string value, Temp temp, object obj)
  30. {
  31. Console.WriteLine("{{x:{0}, y:{1}, value:'{2}', temp.Name:'{3}', obj:'{4}'}}", x, y, value, temp.Name, obj);
  32. }
  33. }
  34.  
  35. class Temp
  36. {
  37. public string Name { get; set; }
  38. }

Autofac之自动装配的更多相关文章

  1. Autofac 组件、服务、自动装配 《第二篇》

    一.组件 创建出来的对象需要从组件中来获取,组件的创建有如下4种(延续第一篇的Demo,仅仅变动所贴出的代码)方式: 1.类型创建RegisterType AutoFac能够通过反射检查一个类型,选择 ...

  2. Autofac 组件、服务、自动装配(2)

    一.组件 创建出来的对象需要从组件中来获取,组件的创建有如下4种(延续第一篇的Demo,仅仅变动所贴出的代码)方式: 1.类型创建RegisterType AutoFac能够通过反射检查一个类型,选择 ...

  3. Autofac 组件、服务、自动装配

    一.组件 创建出来的对象需要从组件中来获取,组件的创建有如下4种(延续第一篇的Demo,仅仅变动所贴出的代码)方式: 1.类型创建RegisterType AutoFac能够通过反射检查一个类型,选择 ...

  4. 【译】Spring 4 自动装配、自动检测、组件扫描示例

    前言 译文链接:http://websystique.com/spring/spring-auto-detection-autowire-component-scanning-example-with ...

  5. 隐式的bean发现与自动装配机制

    使用beans.xml文件进行bean的创建和注入通常是可行的,但在便利性上Spring提供了更简单的方法--自动装配 接下来我们假设一个场景:我有若干播放器(MediaPlayer{CD播放器/MP ...

  6. Spring学习记录(十一)---使用注解和自动装配

    Spring支持用注解配置Bean,更简便. 上面的组件,是根据实际情况配的.比如写的一个类,是做业务处理的,那就用注解@Service表示服务层组件,以此类推.将整体分成不同部分. 要在xml加入c ...

  7. Spring学习记录(三)---bean自动装配autowire

    Spring IoC容器可以自动装配(autowire)相互协作bean之间的关联关系,少写几个ref autowire: no ---默认情况,不自动装配,通过ref手动引用 byName---根据 ...

  8. Spring(二)scope、集合注入、自动装配、生命周期

    原文链接:http://www.orlion.ga/189/ 一.scope bean的scope属性中常用的有两种:singleton(单例,默认)和prototype(原型,每次创建新对象) 例: ...

  9. 自动装配[@Autowired]的歧义性

    在使用@Autowired自动装配时,如果一个接口有多个实现类,那么自动装配就会出现错误,因为Spring无法判断到底要装配哪个实现类实例(bean). 1.可以使用@Qualifier(" ...

随机推荐

  1. web.xml配置文件中的async-supportedtrueasync-supported

    web.xml标题头替换为: <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" ...

  2. 7系列FPGA的时钟资源——UG472

    时钟架构总览 7系的FPGA使用了专用的全局(Global)和区域(Regional)IO和时钟资源来管理设计中各种的时钟需求.Clock Management Tiles(CMT)提供了时钟合成(C ...

  3. 我实在不懂Python的Asyncio

    原语 事件循环(Event Loop) Awaitables和Coroutines Coroutine Wrappers Awaitables and Futures Tasks Handles Ex ...

  4. 前端Vue 源码分析-逻辑层

    Vue 源码分析-逻辑层 预期的效果: 监听input的输入,input在输入的时候,会触发 watch与computed函数,并且会更新原始的input的数值.所以直接跟input相关的处理就有3处 ...

  5. left join on and 与 left join on where的区别

    数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户.       在使用left jion时,on和where条件的区别如下: 1. on条件是在生成临时 ...

  6. Spring AOP概念理解

    1.我所知道的aop 初看aop,上来就是一大堆术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下子让你不知所措,心想着:怪不得很多人都和我说aop多难多难.当我看进去以后 ...

  7. Android CameraManager 类

    先看代码: private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private CameraManager ...

  8. kvm虚拟化1

    计算机的五大组成部分: 运算器,控制器,存储器,输入,输出 虚拟化是对cpu   ,内存,,磁盘, 网络,IO 的虚拟 cpu的虚拟 以时间分片形式进行,这样使得cpu可以运行多个进程 内存进行了空间 ...

  9. spark actions 算子

    package action; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaPairRDD; imp ...

  10. web.xml中Servlet4.0版本的头信息格式

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmln ...