前言
autofac
Autofac是一套高效的依赖注入框架。
Autofac官方网站:http://autofac.org/
Autofac在Github上的开源项目:https://github.com/autofac/Autofac
Autofac安装:通过VS的Nuget可以很方便的获取。
生命周期事件
autofac为注册的类型对象提供了一套生命周期事件,覆盖了一个类型从注册到最后“释放”的一套事件。有了这些事件,我们可以相对方便的在类型对象的各个阶段进行AOP操作。
五大事件
OnRegistered
在类型注册成功后触发,也就是在调用ContainerBuilder的Build方法时,其方法内部触发的。OnRegistered的委托参数类型为ComponentRegisteredEventArgs,其中包含了类型注册后的底层配置信息,此处不对配置信息做介绍,日常一般不会使用这写参数。如果我们希望在类型注册到autofac中后执行一些操作,我们可以通过OnRegistered事件达到目的。关于此事件,在autofac官网上没有相关说明。
var builder = new ContainerBuilder(); builder .RegisterType<A>() //class A { } .OnRegistered(e => { Console.WriteLine(e); }); builder.Build(); //会触发OnRegistered事件OnPreparing
在调用Resolve时触发,具体触发时机,是根据Resolve的类型获取到类型相关配置时触发的,而这时,类型对象还没有实例化。OnPreparing委托参数类型为PreparingEventArgs,该类型有三个属性Component、Context、Parameter,其中Component为Resolve类型的说明/配置,Parameter为Resolve时传入的参数(详见解析获取传参)。在OnPreparing中,我们可以修改传入的Parameter值,甚至可以以此修改实际调用的构造方法(通过Resolve对象构造方法选择原则):
class Program { static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<A>() .OnPreparing(e => { // 1.不做任何处理时,最后输出 result: 3 // 2.修改传入值,最后输出 result: 5 e.Parameters = new[] {new NamedParameter("x", 2), new NamedParameter("y", 3)}; // 3.修改参数类型,最后输出 id: 7, name: 'undefined' e.Parameters = new Parameter[] {new PositionalParameter(0, 7), new TypedParameter(typeof (string), "undefined")}; // 4.直接不要参数,最后输出 no parameter constructor e.Parameters = Enumerable.Empty<Parameter>(); }); var container = builder.Build(); container.Resolve<A>(new NamedParameter("x", 1), new NamedParameter("y", 2)); Console.Write("Press any key to continue..."); Console.ReadKey(); } }class A { public A() { Console.WriteLine("no parameter constructor"); } public A(int id, string name) { Console.WriteLine("id: {0}, name: '{1}'", id, name); } public A(int x, int y) { Console.WriteLine("result: {0}", x + y); } }OnPreparing事件在autofac官网上也没有相关的说明,更多的用法待大家使用过程中进行开荒了。
OnActivating & OnActived
OnActivating与OnActived是两个不同的事件,这里将它们放到一起讲,是因为它们有一定的相似之处,起初用起来还发现不了他们之前的区别,所以放在一起进行对比说明。
相同点
与OnPreparing事件相同,OnActivating与OnActived也是在调用Resolve时触发的,只是OnActivating与OnActived触发是,类型实例已经创建,委托参数的类型虽然不同,但是都有且仅有四个相同类型的属性Parameters、Component、Context、Instance,前三个参数与OnPreparing的委托参数属性类型相同,最后一个Instance是根据Resolve传入的类型实例化出来的实例对象,也就是最后Resolve方法返回的对象。
不同点
不同点主要有两个,一个是它们的委托参数类型不同,虽然它们的属性类型和个数都是相同的,但是OnActivating的委托参数还有一个ReplaceInstance方法,这个方法是用来替换最后返回的对象的,也就是相同点中说到的Instance属性对象。但是需要注意,使用ReplaceInstance时,不是可以替换为任意类型,而是只能替换为相同类型或其子类:
var builder = new ContainerBuilder(); builder.RegisterType<C1>().As<IInterface>() .OnActivating(e => { //e.ReplaceInstance(new C2()); // 异常 //e.ReplaceInstance(new C1()); // 无异常 //e.ReplaceInstance(new CC1()); //无异常 }); var container = builder.Build(); var inter = container.Resolve<IInterface>();interface IInterface { } class C1 : IInterface { } class C2 : IInterface { } class CC1 : C1 { }ReplaceInstance方法是OnActivating与OnActived第一个不同点。关于第二个不同点,现在暂时只发现这个区别与自动属性注入有关,因为自动属性注入实际上是作为一个委托注册到OnActivating或OnActived事件中的,为什么是或呢?因为根据调用PropertiesAutowired方法时,传入的不同参数,这个委托将注册不到不同事件上,如果不传参或传入PropertyWiringOptions.None,自动属性注入,将会注册到OnActivating事件上,否则将注册到OnActived事件上。
关于PropertiesAutowired方法的参数,那是与环形依赖注入相关的,现在暂时不做说明,后续博文将会说明。
PropertiesAutowired注册时机注意
因为自动属性注入是注册到事件上的,然后我们又知道,注册到事件上的委托,一般是顺序调用的,所以需要注意PropertiesAutowired在注册时调用的时机。比如写到自定义注册OnActivating事件前,且PropertiesAutowired方法不传入参数,那么自定义注册的OnActivating事件触发时,参数的Instance中自动注入的属性将已经赋值,反之写到自定义注册OnActivating事件后,自动注入的属性将为赋值!(OnActived事件同理)
var builder = new ContainerBuilder(); builder.RegisterType<C1>() //.PropertiesAutowired() // 在OnActivating前,将输出 OnActivating: not null .OnActivating(e => { // 输出 OnActivating: null Console.WriteLine("OnActivating: " + (e.Instance.C2 == null ? "null" : "not null")); }) .PropertiesAutowired(); // 在OnActivating后,将输出 OnActivating: null builder.RegisterType<C2>(); var container = builder.Build(); var c1 = container.Resolve<C1>();class C1 { public C2 C2 { get; set; } } class C2 { }官网说明
OnActivating与OnActived在autofac官网中有一些说明,可以作为参考:
OnActivating事件中推荐的三种操作:1.替换实例对象,或使用代理对象(通过ReplaceInstance方法);2.执行属性注入或方法注入;3.执行其他初始化任务。
OnActived事件中可以执行一些应用程序级别的任务。
OnRelease
在生命范围结束时调用,关于此事件,将在后续的单元控制文章中进行详细说明,这里暂时不做说明。
统一事件处理方式
上面说的都是为每个类型注册事件,但是如果我们希望为所有类型都注册某一事件,有什么方式来实现呢?
(首先申明,OnRelease事件暂时没找到统一注册的方式)
我们可以在builder注册类型前使用RegisterCallback进行统一事件注册,详见代码:
var builder = new ContainerBuilder(); builder.RegisterCallback(cr => { // 下面的Registered事件相当类型的OnRegistered事件 cr.Registered += (sender, eventArgs) => { // OnPreparing事件 eventArgs.ComponentRegistration.Preparing += (o, preparingEventArgs) => { }; // OnActivating事件 eventArgs.ComponentRegistration.Activating += (o, activatingEventArgs) => { }; // OnActivated事件 eventArgs.ComponentRegistration.Activated += (o, activatedEventArgs) => { }; }; }); // builder.RegisterType<...>... // ...
尾述
autofac提供的这些事件,我们可以很方便的进行AOP操作,比如通过统一事件注册,我们可以很方便的进行日志记录。而关于OnActivating事件和OnActived事件,个人还没有找到具体差别,后续还需要从底层代码来看差别,暂时没有看出,有知道的朋友,烦请告知下,谢谢!