• 跟我一起学.NetCore之依赖注入作用域和对象释放


    前言

        上一小节简单阐述了依赖注入及Asp.NetCore中自带依赖注入组件的常规用法,其中提到容器管控了自己创建对象的生命周期,包含了三种生命周期:Singleton、Scoped、Transient, 对于Singleton、Transient相对于Scoped来说比较好理解,其实这里面有一个作用域的概念,也可以理解为根容器和子容器的范围;上一小节中有一个例子中说到,当注入的生命周期为Scoped的时,在同一个请求内,注入的对象都是同一个,这里Asp.NetCore将每个请求作为了一个作用域,在此作用域内,生命周期为Scoped的对象就是同一个;下面简单说说作用域和对象释放的常规知识点;

    正文

        作用域这里可以理解为服务范围,由IServiceScope承载,如其源代码所示,每一个服务范围内都一个根容器,如下所示:

    // 摘要:
        //     /// The System.IDisposable.Dispose method ends the scope lifetime. Once Dispose
        //     /// is called, any scoped services that have been resolved from /// Microsoft.Extensions.DependencyInjection.IServiceScope.ServiceProvider
        //     will be /// disposed. ///
        public interface IServiceScope : Object, IDisposable
        {
            // IServiceProvider 即代表容器
            //
            // 摘要:
            //     /// The System.IServiceProvider used to resolve dependencies from the scope.
            //     ///
            IServiceProvider ServiceProvider
            {
                get;
            }
        }

        每一个服务范围内可以通过自身IServiceProvider创建对应的子作用域,而任何一个子作用域的IServiceProvider都有对根容器的引用,如下图结构:

        每一个IServiceProvider中都会将其创建的对象存入列表中,针对于继承与IDisposable接口的类型对象会单独存放在一个列表中,当作用域IServiceScope对象的Dispose方法被调用时,最终对应IServicePorvider对应的对象列表也会清空,针对于继承与IDisposable类型的对象会调用其Dispose方法;最后导致对应作用域下容器创建的对象成为垃圾对象,被GC给回收;

        在Asp.NetCore中,框架默认将其分有根作用域(与引用程序同生命周期)和请求作用域(每一个请求一个作用域),通常也会称其为根容器和请求容器,名称分别为ApplicationServices和RequestServices,在程序中获取方式如下:

    •     通过IApplicationBuilder对象可以获取ApplicationServices

    •     通过HttpContext.RequestServices获取RequestServices

        作用域简单举例,在请求作用域下再创建子作用域,分别查看对应不同生命周期对象是否一致,新建一个WebApi项目,针对三个生命周期添加了对应文件,结构如下:

        

    对应红框内的文件内容如下:

           细心的小伙伴可能会看到,每个实现类里面都继承了IDisposable接口,这主要是后面显示释放用的,这里先不管;编辑好以上内容之后就将其进行注册,如下:

        接下来就是使用了,这里通过这Action中使用,在当前的请求作用域下创建子作用域,对比每一个生命周期对象,如代码所示:

    [HttpGet]
    public string Get([FromServices]ITestSingleton testSingleton, [FromServices]ITestSingleton testSingleton1,
                      [FromServices]ITestScoped testScoped, [FromServices]ITestScoped testScoped1,
                      [FromServices]ITestTransient testTransient, [FromServices]ITestTransient testTransient1)
    {
        //获取请求作用域(请求容器)
        var requestServices = HttpContext.RequestServices;
        //在请求作用域下创建子作用域
        using(IServiceScope scope = requestServices.CreateScope())
        {
            //在子作用域中通过其容器获取注入的不同生命周期对象
            ITestSingleton testSingleton11 = scope.ServiceProvider.GetService<ITestSingleton>();
            ITestScoped testScoped11 = scope.ServiceProvider.GetService<ITestScoped>();
            ITestTransient testTransient11 = scope.ServiceProvider.GetService<ITestTransient>();
    ​
            ITestSingleton testSingleton12 = scope.ServiceProvider.GetService<ITestSingleton>();
            ITestScoped testScoped12 = scope.ServiceProvider.GetService<ITestScoped>();
            ITestTransient testTransient12 = scope.ServiceProvider.GetService<ITestTransient>();
            Console.WriteLine("================Singleton=============");
            Console.WriteLine($"请求作用域的ITestSingleton对象:{testSingleton.GetHashCode()}");
            Console.WriteLine($"请求作用域的ITestSingleton1对象:{testSingleton1.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestSingleton11对象:{testSingleton11.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestSingleton12对象:{testSingleton12.GetHashCode()}");
            Console.WriteLine("================Scoped=============");
            Console.WriteLine($"请求作用域的ITestScoped对象:{testScoped.GetHashCode()}");
            Console.WriteLine($"请求作用域的ITestScoped1对象:{testScoped1.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestScoped11对象:{testScoped11.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestScoped12对象:{testScoped12.GetHashCode()}");
            Console.WriteLine("================Transient=============");
            Console.WriteLine($"请求作用域的ITestTransient对象:{testTransient.GetHashCode()}");
            Console.WriteLine($"请求作用域的ITestTransient1对象:{testTransient1.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestTransient11对象:{testTransient11.GetHashCode()}");
            Console.WriteLine($"请求作用域下子作用域的ITestTransient12对象:{testTransient12.GetHashCode()}");
        }
    ​
        return "TestServiceScope";
    ​
    }

        运行,进行请求,看打印结果:

    •     对于Singleton来说始终不变,因为其是跟随根容器生命周期,引用程序退出才释放;

    •     对于Scoped来说只要在自己的作用域内就是单例的;

    •     对于Transient来说始终创建;

        以上一直在说释放,下面利用继承IDisposable接口释放时会调用对应Dispoable方法的原理简单演示各个生命周期的释放时机;在Controller中增加几个Action方法,如下:

        这里使用IHostApplicationLifetime中的StopApplication模拟关闭程序释放单例下的对象;运行看效果:

        使用坑:不要从根容器中获取Transient生命周期的对象,因为通过根容器创建的对象不会回收,除非等到应用程序退出,这样会导致内存泄露;如下演示:

        

    新增Action方法:

    运行,发送请求看结果:

    总结

        作用域及对象释放就简单说这么多,容器只管理自己创建出来的对象生命周期;下一节说说使用第三方组件扩展依赖注入功能;

  • 相关阅读:
    VSCode 预览 .md 文件
    ubuntu 16.04安装visual studio code 提示libnss3版本低:NSS >= 3.26 is required
    spring-tool-suite(STS) 创建 spring boot项目
    win10正式版开始菜单无法打开,右边的网络连接、操作中心也打不开
    nginx配置事例
    spring 集成 redis -- pub/sub
    springMVC--全局异常处理
    dubbo入门使用
    dubbo配置约束
    zookeeper安装与集群搭建
  • 原文地址:https://www.cnblogs.com/zoe-zyq/p/13392299.html
Copyright © 2020-2023  润新知