深入 Unity 1.x 依赖注入容器之二:初始化 Unity
原文链接:http://www.doriandeng.cn/archives/97.html
Unity 初始化主要是注册类型映射并指定其生命周期。
在本文中,我们使用了一个接口 IDialer、一个实现了接口的抽象基类 Dialer,二个继承自 Dialer 的具体类 ButtonTypeDialer 和 FigurePlateDialer 类,以及一个使用 Dialer 的 Telephone 类。
生命周期管理
之所以将生命周期的管理放在开始,是因为Unity 会根据在类型的注册时需要指定的生命周期来管理对象的创建和解析。
Unity 使用继承自 LifetimeManager 基类的类来决定如何保存对象实例的引用,以及如何销毁对象。Unity 自带了二个用于生命周期管理的类:ContainerControlledLifetimeManager 和 ExternallyControlledLifetimeManager ,下面分别对这二个类进行描述。如果指定了这二种管理方式,无论是以 RegisterType 还是以 RegisterInstance 注册的类型,都将处理成单件对象。
ContainerControlledLifetimeManager 类是指定 Unity 容器管理注册的对象,即对象的生命周期与 Unity 容器一致,在销毁 Unity 时将销毁所有其管理的对象。同时,
ExternallyControlledLifetimeManager 类指定 Unity 仅保持对象的弱引用,对象的保存和销毁由 Unity 容器外部来控制。
如果不需要处理成单件对象,可以在调用 RegisterType 方法时不指定其 LifetimeManager 类,这样 Unity 容器将使用临时生命周期管理,即为每次对获取对象的请求都创建一个新的实例。
用 RegisterType 注册类型映射
RegisterType 包含多个支持泛型的重载,同时还包含了一一对应的非泛型重载。在此仅对其泛型重载进行阐述,非泛型重载与其对应。
RegisterType 注册类型映射可以是注册接口或基类所对应的类型,也可以直接注册类型。对于前者,在此仅给出接口或基类注册的一种,另一种直接替换即可。
RegisterType<TFrom, TTo>( )
此方法注册一个默认的类型映射,并且为获取对象的每次请求创建一个新的实例。例如如下代码:
在上例中的代码中,前二行即是对类型的注册。运行此代码,结果如下:
而这种注册的对应的配置文件如下:
同时,将上面的代码修改为如下代码:
运行此代码,我们将得到同样的结果。
RegisterType<TFrom, TTo>(LifetimeManager lifetime)
此方法与上一方法的不同之处在于可以指定生命周期的管理。例如,我们有如下代码:
我们将可以得到如下结果:
可以看出,在使用 ContainerControlledLifetimeManager 之后,二次获取的都是同一对象,即 Unity 容器在第一次获取时创建了一个对象后就将其保存了下来供后继获取使用。
然后,我们将这几行语句拆成二个方法,如下所示:
这时,我们可以得到与前面同样的结果,读者可以将其中的 ContainerControlledLifetimeManager 类替换成 ExternallyControlledLifetimeManager 类检查这二个类所产生的不同效果。
如果要使用配置文件来完成注册,我们可以使用如下配置:
同时,我们需要将上面的第 64 行代码修改第22、23行代码以应用配置文件。
RegisterType<TFrom, TTo>(String name)
此方法用于注册一个命名的类型映射,而不是默认的类型注册。如果 name 为 null,其作用将与 RegisterType<TFrom, TTo>( ) 方法相同。示例代码如下:
从上面的代码可以看出,此方法的不同点在于在获取对象的时候需要指定其类型注册的名称。相应的配置文件如下:
RegisterType<TFrom, TTo>(String name, LifetimeManager lifetime)
此方法是前面三种方法的组合,在此不再详述。
其他
其他方法还包括 RegisterType<T>(LifetimeManager lifetime)、RegisterType<T>(String name, LifetimeManager lifetime)等方法,使用方法与前面类似,不再详述。
用 RegisterInstance 注册已存在的对象实例
RegisterInstance 用于将一个已创建的对象实例注册成单件类型映射,默认情况下,使用 ContainerControlledLifetimeManager 生命周期管理。
RegisterInstance<T>(T instance)
此方法注册一个默认的单件类型映射。示例代码如下:
在此示例中,我们先创建了一个 ButtonTypeDialer 类型的对象,然后将其注册为 Dialer 类型的映射,然后通过 Unity 来获取它。
与 RegisterType 不同的是,它的配置文件如下所示:
在配置文件中,我们使用了一个 DialerConverter 类,这是一个类型转换类。在 Instances 元素中 value 元素是必须的,如果要创建一个自定义类型的实例,就必须有一个合适的类型转换类来将字段串表示的 value 值转换成需要的类型。相应的,我们的代码修改如下:
注意:在Resolve 方法中指定的类型必须与配置文件中的类型一致,否则会触发异常。
RegisterInstance<TInterface>(TInterface instance, LifetimeManager lifetime)
此方法在注册一个已有对象的实例的类型映射的同时指定一个生命周期管理。示例代码如下:
大家可以将 ContainerControlledLifetimeManager 替换成 ExternallyControllerLifetimeManager 试试,这时会出现异常。
这个方法对应的配置文件是,遗憾的是,配置文件不支持此方法的生命周期的配置。
其他
其他还有 RegisterInstance<TInterface>(String name, TInterface instance) 和 RegisterInstance<TInterface>(String name, TInterface instance, LifetimeManager lifetime) 等方法,大家可以参考 RegisterType 方法进行学习。
Unity 容器的层次
Unity 容器支持容器的嵌套,那么在什么情况下需要使用容器的嵌套呢。如针对同一类型注册多个类型映射,类型映射需要有不同的生命周期管理时,都需要使用嵌套。下面仅对同一类型注册多个类型映射的情况进行解释。
示例代码如下:
运行代码,我们可以得到如下的结果:
可能有人会想,这样还不如直接创建二个容器,但之所以这么做,是为了所有对象都可以在父容器中进行管理,而且,子容器多用于管理一些临时性的映射。另外,可以在子容器使用在你父容器中注册的映射,也就是说,容器会自己向上搜索注册。
Unity 的配置文件并不直接支持容器的嵌套,这时,我们可以通过在配置文件中将父容器定义成默认的容器,而将子容器配置成命名的容器,然后将命名配置应用到子容器上即可。例如,我们有如下配置:
通过将代码修改如下,我们将可以得到同样的结果:
小结
通过上面的学习,我们已经全面了解了 Unity 依赖注入容器的初始化,在经过这些不同的注册后,我们将可以通过不同的方式获取对象,使用对象。
源代码下载
look-into-unity-1-1-initunity.zip
希望对您有所帮助!
邓明