.Net core 依赖注入

3 篇文章 0 订阅

Dependency Inject 概述

依赖注入( DI )是一种编程模式,指将原本本类中依赖的对象通过外部传入,以达到解耦的目的,因为创建对象的控制权发生了变换(由原本是当前类来控制对象如何创建,已经如何销毁). 这样会引发一些问题, 比如对象应该在何时被创建,何时销毁(也就是依赖对象的生命周期).

IOC 与 DI

引用 ASP.NET Core 框架解密 中的一段话, Ioc 主要体现了这样一种思想: 通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照好莱坞法则实现应用程序的代码与框架之间的交互. 我们可以采用若干设计模式以不同的方式实现 Ioc 如模板方法, 工厂方法和抽象工厂, 下面介绍一种更具有价值的Ioc模式:依赖注入(DI). 这样看 DI 只是 Ioc 的一种实现方式, Ioc 是从更宏观的角度去考虑应用程序和框架应该怎样去设计, 而为DI是解决对象依赖的具体问题而生的

Microsoft.Extensions.DependencyInjection

微软为我们提供的 DI 类库位于 Microsoft.Extensions.DependencyInjection, 和 Microsoft.Extensions.DependencyInjection.Abstractions 这个类库里面,其中 后缀为 Abstractions的里面提供了相应的接口生命,而实现则在 DependencyInjection, 我们看一段简单的代码,来了解下使用方式.


using Microsoft.Extensions.DependencyInjection;
using System;

namespace Demo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var serviceProviderFactory = new DefaultServiceProviderFactory();

            var services = new ServiceCollection();
            services.AddScoped<ILogger, Logger>();

            var serviceProvider = serviceProviderFactory.CreateServiceProvider(services);

            var logger = serviceProvider.GetRequiredService<ILogger>();

            logger.Log();

            Console.ReadKey();
        }
    }

    public interface ILogger
    {
        void Log();
    }

    public class Logger : ILogger
    {
        public void Log()
        {
            Console.WriteLine("logger");
        }
    }
}

程序正常输出

logger

上述代码中

  1. 先是创建了一个 DefaultServiceProviderFactory 的对象,
  2. 创建了一个 ServiceCollection 的对象 services
  3. 将我们需要依赖的对象 Logger 添加到 services 对象中, 并对外提供一个接口 Ilogger
  4. serviceProviderFactory 该对象为我们提供了一个方法 CreateServiceProvider() 用于创建服务提供者, 接受一个 ServiceCollection 对象作为参数
  5. 通过 serviceProviderGetRequiredService() 方法 获取 ILogger 接口
  6. 调用 ILogger 接口的 Log() 方法,在控制台上打印log

上面是使用依赖注入DI的基本使用方法. 我们使用的 AddScoped() 方法是以 Scoped 方式将 Logger 加入到了 DI 容器中, 这里的容器指的是 ServiceCollection. 下面我们看下一共有几种生命周期的方式

  1. AddSingleton() 以单例的方式将类型注入到容器中, 该类型的对象在整个程序中是唯一的
  2. AddScoped() 在一个范围内保持从容器中获取某一个类型的对象是唯一的
  3. AddTransient() 每次从容器中获取某一个类型的对象时,都获取新对象

AddSingleton(), AddTransient() 这两种方式都好理解,相对于我们传统的创建对象的方式, Singleton 是单例模式, Transient 是每次都会 new 一个新的对象. 那么 Scoped 中 这个范围是什么呢, 我们来看看

public static IServiceCollection AddScoped(
    this IServiceCollection services,
    Type serviceType,
    Type implementationType)
{
    //...省略若干代码

    return Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
}

private static IServiceCollection Add(
    IServiceCollection collection,
    Type serviceType,
    Type implementationType,
    ServiceLifetime lifetime)
{
    var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
    collection.Add(descriptor);
    return collection;
}

IServiceCollection

IServiceCollection 是实现了 IList<ServiceDescriptor> 接口的, 所以本质上他是包裹了ServiceDescriptor 的集合,向 IServiceCollection 添加类型注册, 实际上就是添加 ServiceDescriptor

ServiceDescriptor

ServiceDescriptor 用来描述一个服务注册时候的信息,我们可以手动调用IServiceCollectionAdd()方法向容器中添加ServiceDescriptor对象. 下面是 ServiceDescriptor的部分代码

public class ServiceDescriptor
{
    //... 省略若干代码
    public ServiceLifetime Lifetime { get; }

    public Type ServiceType { get; }

    public Type ImplementationType { get; }

    public object ImplementationInstance { get; }

    public Func<IServiceProvider, object> ImplementationFactory { get; }

    //... 省略若干代码
}

从上面的代码可以看出,

  1. Lifetime 表示生命周期
  2. ServiceType 表示注册服务的类型
  3. ImplementationType 表示服务注册类型实现的类型
  4. ImplementationInstance 表示服务注册类型实现的对象,例如:直接注入自己创建的对象.
  5. ImplementationFactory 表示服务实现的工厂,使用一个委托类型Func<IServiceProvider, object>来表示

ServiceDescriptor 对象还提供了很多静态方法,方便我们构造ServiceDescriptor对象,代码如下:

public class ServiceDescriptor
{
    public static ServiceDescriptor Scoped<TService, TImplementation>()
        where TService : class
        where TImplementation : class, TService
    {  
        // ...
    }

    public static ServiceDescriptor Singleton<TService, TImplementation>()
        where TService : class
        where TImplementation : class, TService
    {
        // ...
    }

    public static ServiceDescriptor Transient(Type service, Func<IServiceProvider, object> implementationFactory)
    {
        // ...
    }
}

ServiceProvider

ServiceProvider 继承自 IServiceProvider 实现了 GetService() 方法, 但真正提供提供获取服务对象的并不是他, 他只是一个Enging的工厂, GetService() 方法调用的也是 EngingGetService() 方法, 默认使用的是 DynamicServiceProviderEngine, 他是 ServiceProviderEngine 的子类

ServiceProviderEngine

ServiceProviderEngine 也实现了 IServiceProvider 接口, 作为跟容器, 封装着一个名为 RootServiceProviderEngineScope 对象,

internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory
{
    //... 省略若干代码
    public ServiceProviderEngineScope Root { get; }

    public IServiceScope RootScope => Root;

    protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback)
    { 
        //... 省略若干代码
        Root = new ServiceProviderEngineScope(this); 
        //... 省略若干代码
    }

    public object GetService(Type serviceType) => GetService(serviceType, Root);

    public IServiceScope CreateScope()
    {
        if (_disposed)
        {
            ThrowHelper.ThrowObjectDisposedException();
        }

        return new ServiceProviderEngineScope(this);
    }


    //... 省略若干代码
}

ServiceProviderEngineScope

ServiceProviderEngineScope 对象维护着 以下两个关键的字段 _disposables 字段, 当调用 GetService() 获取一个对象的时候, 如果这个对象实现了 IDisposable 接口,则该对象会被添加到 _disposables 里面. 当自身的Dispose()方法被调用时, 他会循环调用 _disposables 里对象的 Dispose() 方法 以释放依赖 scope 范围内的对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值