http://code.google.com/p/autofac/wiki/ComponentCreation
autofac容器提供多个内置参数用来创造Component。
Component可以通过两种方式被创建:
ContainerBuilder 提供了 Register() 一类的方法去创建 Component。
ContainerBuilder使用 As 方法将Component封装成了服务使用。
1
2
3
|
var builder =
new
ContainerBuilder();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<NHPersonRepository>().As<IFindPerson, IRepository<Person>>();
|
使用 As() 方法将Component的默认服务覆盖掉.所以
1
|
container.Resolve<ILogger>();
|
上面的语法会成功.但是
1
|
container.Resolve<ConsoleLogger>();
|
不会.
AutoFac能够通过反射检查一个类型,选择一个合适的构造函数,创造这个对象的实例. RegisterType<T>() 和 RegisterType(Type) 两个方法以这种方式建立.
1
2
|
builder.RegisterType<A>();
// Create A using reflection
builder.RegisterType(
typeof
(B));
// Non-generic version
|
选择构造函数和实例化依赖的过程会在后面的原理中讲述.
默认服务
通过反射创造的默认实例会是默认实现类型(A above will provide the service A)
发射对于component创建来说是个很不错的默认选择.事情变得乱七八糟尽管component创建逻辑建立在简单的构造函数上.
Autofac接受一个委托或者lambda表达式用来创建component.
builder.Register(c =>
new
A(c.Resolve<B>()));
|
表达式中的参数 c 是创建component的容器.使用这种方式而不是闭包()去访问容器非常重要.这确保了精确配置(DeterministicDisposal)和嵌套容器可以被正确支持.
使用这个参数可以满足额外的依赖,比如在这个例子中A需要B作为构造参数.
下面是一些用反射创建component的一些例子(很糟糕的需求)
复杂的参数
构造函数的参数不能使用简单的常量定义.与其困惑于如何在XML配置文件中构造一个特定类型的值,不如使用C#:
builder.Register(c =>
new
UserSession(DateTime.Now.AddMinutes(25)));
|
(当然session的终结是你很有可能想在xml配置文件中制定的)
属性注入
这是推荐的属性初始化方式. IComponentContext.ResolveOptional() 很方便:
builder.Register(c =>
new
A(){ MyB = c.ResolveOptional<B>() });
|
或者, PropertiesAutowired() 这个方法也可以导致属性注入.
builder.RegisterType<X>().PropertiesAutowired();
|
不管怎样,在大多数情况下不推荐使用属性注入.替代的选择像 Null Object -使用构造函数注入重载构造函数或者构造函数参数默认值来创造一个干净的,"不变的"components.
根据参数来选择实现方法
使用IOC容器创建对象的最大好处之一就是一个类型可以被实例化成不同的实例,这通常体现在运行阶段,而不是配置阶段.
1
2
3
4
5
6
7
|
builder.Register<CreditCard>((c, p) => {
var accountId = p.Named<
string
>(
"accountId"
);
if
(accountId.StartsWith(
"9"
))
return
new
GoldCard(accountId);
else
return
new
StandardCard(accountId);
});
|
在这个例子中,信用卡被是实例化成两个类:金卡和普通卡.这是由运行时段提供的卡ID决定的.
在这个例子中,一个可选的参数P被提供给构造函数.
可以像下面这样使用这种注册方法来获得实例:
1
|
var card = container.Resolve<CreditCard>(
new
NamedParameter(
"accountId"
,
"12345"
));
|
当使用委托工厂或者使用一个委托来创建CreditCard的实例时,可以使语言清晰,而且是强类型的.
Default Service
使用这种表达式创建的component默认值是由表达式推导出来的.
有时候我们需要将autofac集成到一个系统中.在很多情况下,我们需要在容器中使用一些已经在系统存在的单例对象的实例.这时就不能创造一个单例的component(因为这些对象已经在系统中存在了),而是应该在容器中将它们注册为实例:
1
|
builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();
|
这种方法会确保系统中的单例实例最终转化为由容器托管的单例实例.
Default Service
默认值就是注册的那个实例
autofac支持开放的泛型类型.使用 RegisterGeneric() 这个方法:
1
2
3
|
builder.RegisterGeneric(
typeof
(NHibernateRepository<>))
.As(
typeof
(IRepository<>))
.InstancePerLifetimeScope();
|
当从容器中请求一个匹配的类型时,autofac会自动匹配一个等价的具体实现类型.
1
2
|
// Autofac will return an NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();
|
注册一个具体的类型((e.g. IRepository<Person> )会覆盖掉上面的泛型注册.
如果一个类型被多次注册,以最后注册的为准.
为避免各种情况,可以使用 PreserveExistingDefaults() 修饰符
1
2
|
builder.Register<X1>().As<IX>();
builder.Register<X2>().As<IX>().PreserveExistingDefaults();
|
在这种情况下,X1会是IX的默认值.(如果不适用 PreserveExistingDefaults() 修饰符,X2会是默认值,因为它是最后被注册的)