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

默认

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

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

class ConstructorClass
{
private Class1 _clas1;
private Class2 _clas2;
private Class3 _clas3 = null; public ConstructorClass()
{
_clas1 = null; _clas2 = new Class2 { Id = - };
} public ConstructorClass(Class1 clas1, Class2 clas2)
{
_clas1 = clas1; _clas2 = clas2;
} public ConstructorClass(Class2 clas2, Class3 clas3)
{
_clas2 = clas2; _clas3 = clas3;
} public ConstructorClass(Class2 clas2, Class3 clas3, Guid guid)
{
_clas1 = new Class1 { Id = guid }; _clas2 = clas2; _clas3 = clas3;
} public ConstructorClass(Class1 clas1, Class2 clas2, Class3 clas3)
{
_clas1 = clas1; _clas2 = clas2; _clas3 = clas3;
} public override string ToString()
{
return string.Format(
"{{Class1.Id: {0}, Class2.Id: {1}, Class3: {2}}}",
_clas1 == null ? "null" : _clas1.Id.ToString(),
_clas2 == null ? "null" : _clas2.Id.ToString(),
_clas3 == null ? "null" : "not null");
}
} class Class1
{
public Guid Id { get; set; }
} class Class2
{
public int Id { get; set; }
} class Class3
{ }static void Main(string[] args)
static void Main(string[] args)
{
//注册容器
var builder = new ContainerBuilder();
//向容器中注册类型
builder.RegisterType<ConstructorClass>();
builder.RegisterType<Class2>();
builder.RegisterType<Class3>();
using (var container = builder.Build())
{
#region Resolve对象构造方法选择原则(当我们注册的类型拥有多个构造方法,那么在Resolve时,将会以尽可能最多参数构造方法为准)
var obj = container.Resolve<ConstructorClass>();
Console.WriteLine(obj);
#endregion
}
Console.ReadKey();
}

该实例显示,选择的是第三个构造函数,参数为(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)的构造函数

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

额外的构造函数参数

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

注册时添加参数

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

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

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

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

自动装配

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

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使用的示例供参考:

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

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. MySql存储过程及函数

    存储过程和函数类似于Java中的方法. ⒈存储过程 一组预先编译好的sql语句的集合,理解成批处理语句. 好处: ①提高代码的重用性 ②简化操作 ③减少了编译次数并且减少了和数据库服务器的连接次数,提 ...

  2. [Kubernetes]编排其实很简单

    什么是编排 Kubernetes中,我们总是在说一个概念:编排. 在[Kubernetes]谈谈Kubernetes的本质这篇文章中,关于"编排"的概念介绍了一下:过去很多集群管理 ...

  3. CAD版本知识

  4. Lua中的迭代器与泛型for

    [前言] 迭代器就是一种可以遍历一种集合中所有元素的机制,在Lua中,通常将迭代器表示为函数.每调用一次函数,就返回集合中的“下一个”元素.每个迭代器都需要在每次成功调用之后保存一些状态,这样才能知道 ...

  5. python学习第24天

    内置方法 常用 __new__ __del__ __call__ 不常用 __str__ __repr__ __enter__ __exit__

  6. WPF学习:3.Border & Brush

    上一章<WPF学习:2.Layout-Panels-Countainers>主要介绍了布局,容器和面板.这一章主要开始介绍Border(边界)和Brush(画刷). 代码地址:http:/ ...

  7. 阿里云ECS发送邮件到腾讯企业邮箱

    1.安装软件 yum install mailx yum install sendmail 2.配置 vim /etc/mail.rc在文件最后加上如下内容: set smtp=smtps://smt ...

  8. An overview of network penetration testing

    1. an SQLi vulnerability will allow you  to do the  following query the database using select statem ...

  9. JVM 组成以及各部分作用

    1.Java虚拟机的体系结构 2.jvm在系统中的布局 3.jvm装载一个类 当一个classLoder启动的时候,classLoader的生存地点在jvm中的堆,然后它会去主机硬盘上将A.class ...

  10. Linux内核原理与分析-第二周作业

    写之前回看了一遍秒速五厘米:如果