Autofac全面解析系列(版本:3.5) – [使用篇(推荐篇):5.生命周期事件]

前言

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第一个不同点。关于第二个不同点,现在暂时只发现这个区别与自动属性注入有关,因为自动属性注入实际上是作为一个委托注册到OnActivatingOnActived事件中的,为什么是或呢?因为根据调用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事件,个人还没有找到具体差别,后续还需要从底层代码来看差别,暂时没有看出,有知道的朋友,烦请告知下,谢谢!

posted @ 2015-12-24 14:52  An.Cup.Of.Coffee  阅读(2344)  评论(8编辑  收藏  举报
顶部

底部
Copyright ©2016 An.Cup.Of.Coffee