zoukankan      html  css  js  c++  java
  • 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事件,个人还没有找到具体差别,后续还需要从底层代码来看差别,暂时没有看出,有知道的朋友,烦请告知下,谢谢!

  • 相关阅读:
    Tomcat 生产服务器性能优化
    ExtJs3常用控件操作实例
    vss报错Workgroup无法访问,您可能没有权限使用网络资源解决办法
    Java Socket Example
    正则表达式:根据逗号解析CSV并忽略引号内的逗号
    java打jar包 命令行cmd在当前路径打jar包
    【Java/Json】Java对Json进行建模,分词,递归向下解析构建Json对象树
    【Json】Json分词器
    【java/Json】用Java对象构建Json语法树
    【Java文件】按UTF-8编码读取文本文件(逐行方式),排序,打印到控制台
  • 原文地址:https://www.cnblogs.com/ancupofcoffee/p/5072894.html
Copyright © 2011-2022 走看看