最通俗易懂的依赖注入之生命周期

这篇文章是 ASP.NET 6 依赖注入系列文章的第二篇,点击上方蓝字可以阅读整个系列

在上一篇文章中,我们讨论了什么是依赖注入和控制反转,以及它的作用是什么。

在这篇文章中,我们先演示一下依赖注入的基本用法, 然后再讨论生命周期模式。

e8d621a0f2a1b36a0ac2652b25048a30.png

基本用法

.NET 依赖注入组件主要涉及两个包:

<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
  </ItemGroup>

Abstractions 结尾的包中定义的是接口和基础的数据类型,具体实现则是由没有 Abstractions 结尾的包来提供。

比如依赖注入组件,抽象的接口和必要的类型都定义在Microsoft.Extensions.DependencyInjection.Abstractions的包中,具体的实现则在 Microsoft.Extensions.DependencyInjection 包中。

.NET 中的很多类库和组件,在设计的时候都会考虑这种“抽象和实现”的分离。

在 ASP.NET 依赖注入系统中,我们需要通过一个由 IServiceCollection 接口表示的集合来完成服务的注册,所以我们称它为服务集合。

你可以把服务集合理解为一个服务的花名册,服务注册其实就是在这个花名册里,登记服务类型的基本信息,以便在需要的时候,依赖注入控制系统能够找到它。

通过服务集合可以创建出服务提供对象,它表示为一个 ServiceProvider 对象,如下所示:

public  static  class  Sample01
{
    public  interface  IAccount{ }
    public  interface  IMessage{ }
    public  interface  ITool{ }

    public  class  Account: IAccount{}
    public  class  Message: IMessage{}
    public  class  Tool: ITool{}

    public static void Run()
    {
        // 创建服务集合
        var serviceCollection = new ServiceCollection();
        // 注册服务
        serviceCollection.AddTransient<IAccount, Account>();
        serviceCollection.AddScoped<IMessage, Message>();
        serviceCollection.AddSingleton<ITool, Tool>();
        // 创建服务提供对象
        var serviceProvider = serviceCollection.BuildServiceProvider();
    }
}

这段示例代码中,有三个接口,并分别实现了三个类。

这里的每一个接口都可以认为是一项服务,而每个类则是这个服务的具体实现。

我们可以通过服务提供对象,获取任何已经注册过的服务类型的实例,如下所示:

serviceProvider.GetService<IAccount>();
serviceProvider.GetService<IMessage>();
serviceProvider.GetService<ITool>();

服务注册

.NET 依赖注入组件采用了生命周期的方式,来管理它所提供的服务实例。

所谓的生命周期,就是指由依赖注入组件创建出来的服务实例,可以存活多久。

生命周期有三种模式:瞬时(Transient)、作用域(Scoped)、单例(Singleton)。

我们在注册服务时,必须要指定服务属于哪种生命周期模式,比如刚才的代码示例:

serviceCollection.AddTransient<IAccount, Account>();
serviceCollection.AddScoped<IMessage, Message>();
serviceCollection.AddSingleton<ITool, Tool>();

从注册方法的名称Addxxxx可以看出,注册的服务采用的生命周期模式依次为「瞬时\作用域\单例」

由于每个服务都可以有多种实现,所以在进行服务注册时,可以为同一个服务注册多个不同的实现类。

虽然可以注册服务的多个实现类,但是GetService方法只能返回其中一个实现类的实例,也就是最后注册的实现类。

如果想要获取某个服务的所有实现,我们可以看这个示例:

public  static  class Sample02
{
    //...
    public  abstract  class  Base { }

    public  class  Account:Base, IAccount{}
    public  class  Message:Base, IMessage{}
    public  class  Tool:Base, ITool{}

    public static void Run()
    {
        // 创建服务集合
        var serviceCollection = new ServiceCollection();
        // 注册服务
        serviceCollection.AddTransient<Base, Account>();
        serviceCollection.AddScoped<Base, Message>();
        serviceCollection.AddSingleton<Base, Tool>();
        // 创建服务提供对象
        var serviceProvider = serviceCollection.BuildServiceProvider();
        // 获取服务集合
        var services = serviceProvider.GetServices<Base>().ToList();
    }
}

示例中添加了一个 Base 抽象类,其它的类型都是 Base 的具体类。

这种抽象类和具体类的关系,类似接口与实现类的关系,所以也可以注册到依赖注入系统中。

使用GetServices 方法可以获取某个服务的类型集合,注意这个 Service 是复数形式。

示例中返回的是一个 Base 类集合,集合的元素就是已注册的 3 个具体类实例。

生命周期模式

通过前面的示例,我们可以发现,服务注册的方法有多个,每一个方法对应着不同的生命周期。

那么什么是生命周期呢?

简单来说,服务的生命周期代表着每一个服务实例的生存期。

为什么依赖注入系统中的服务实例要定义生命周期呢?

因为依赖注入系统,管理着整个应用的服务实例。

在我们开发应用的时候,肯定用到过单例。被设计为单例模式的对象,在整个应用的生命周期中,有且只有一个。

非单例模式的普通对象,都是随用随建,用完即丢。

既然依赖注入系统管理着整个应用的服务实例,那么不管是高贵的单例对象、还是没有人权普通对象,都是依赖注入系统中的一员。

于是,为了让依赖注入系统分辨出哪个对象属于哪种模式,就有了不同模式的生命周期。

前面我们说过,生命周期的模式有三种:瞬时、作用域和单例。

其中,瞬时和单例理解起来比较简单。

「瞬时,就是没有生存期。」

也就是说,每次从依赖注入系统中获取瞬时的服务实例时,都会创建一个全新的对象。

依赖注入系统中的服务容器不会保存它,也就是没有生存权的普通对象。

「单例,就是会一直存在,与应用同寿。」

也就是说,第一次从依赖注入系统中获取单例的服务实例时,才会创建一个全新的对象。

依赖注入系统中的服务容器会保存它,之后的每次使用都是直接从容器中获取它,也就是高贵的单例对象。

「作用域,理解起来没有那么直观,需要结合场景来说明。」

比如,在 ASP.NET 的应用中,每一个来自外部的请求,都可以理解为是一个请求作用域。不同的请求,就是不同的请求作用域。

在同一个请求作用域中,获取作用域模式的服务实例与单例模式的服务实例,具有同样的表现。

也就是说,第一次从依赖注入系统中获取服务实例时,才会创建一个全新的对象。

依赖注入系统会在服务容器中为该作用域开个单间,单独保存该对象。

当请求结束时,请求作用域会被销毁,单间自然也就没了,其中保存的对象也会随之销毁。

所以,在这种模式中生存的对象实例,都只作用于自己的域范围,不同的域不会互相干涉。

由此可见,服务一旦有了生命周期,那么依赖注入系统就可以根据需求,来保存和管理它们的实例。

更多精彩内容,请关注我▼▼

 
 

ae562763f510bb7da88151a96b62badf.gif

如果喜欢我的文章,那么

在看和转发是对我最大的支持!

(戳下面蓝字阅读)

a9a98a733602227db0f404b47aeaef0c.png

推荐关注微信公众号:码侠江湖

                        75728f6ceb28483c6d53290b5d8a7883.png觉得不错,点个在看再走哟

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring框架通过使用依赖注入(DI)来实现解耦。DI允许外部实体在构造函数,字段或者集合属性注入相应的依赖,从而使得类的实例化更加简单,代码更加清晰,维护更加容易。Spring框架实现DI的关键有三个:控制反转(IoC)、面向切面编程(AOP)和依赖查找(DL)。 ### 回答2: Spring框架是一个开源的Java应用开发框架,它使用了依赖注入的原理来管理对象之间的依赖关系。所谓依赖注入,就是让程序员不再需要手动创建和管理对象之间的关系,而是由框架来自动完成。 在Spring框架中,我们首先需要定义好我们的Java类,声明它们之间的依赖关系。我们可以使用注解的方式,在需要依赖的属性或者构造方法上加上注解,告诉框架这个属性或者参数需要注入一个对象。 当我们启动程序的时候,Spring框架会根据我们的配置信息,遍历所有的Java类,解析其中的注解信息。然后会根据这些信息创建一个对象的实例,并且将需要注入的属性或者参数自动赋值。这个过程是通过Java的反射机制来实现的。 具体来说,Spring框架会根据注解上的信息,找到合适的对象实例,然后通过调用对象的构造方法或者设值方法,将实例注入到被依赖的属性或者参数中。这样,我们就完成了对象之间的依赖关系的建立,可以方便地使用它们进行开发和业务处理。 借助依赖注入,我们不再需要手动创建和管理对象之间的依赖关系,大大简化了对象之间的耦合度。我们只需要关注对象的功能实现,而不需要过多关心它的依赖关系。这样可以提高开发效率,同时也方便了程序的维护和修改。 ### 回答3: Spring框架是一个用于简化Java开发的框架,其中的依赖注入是其中的一个核心特性。 依赖注入是将对象之间的依赖关系交由框架来管理,而不是由开发人员手动创建和管理。在Spring中,依赖注入是通过配置文件或注解的方式来实现的。 首先,需要将要注入的类所对应的bean配置为一个Spring的bean,这样框架就能够管理这个对象的生命周期。配置文件通常是一个XML文件,其中包含了对Bean的定义和属性的设置。 接下来,需要在需要注入的类中声明需要注入的属性,并为这些属性提供setter方法。Spring框架在启动时会扫描配置文件,找到需要注入的类,并创建对应的对象。 当需要使用某个对象时,Spring会自动将需要注入的属性通过反射的方式注入到对象中,而不需要开发人员手动创建和设置依赖关系。 通过注入,对象之间的依赖关系被解耦,每个对象只需要关注自己的业务逻辑,而不需要关心如何获取依赖的对象。这样可以提高代码的可维护性和可测试性,并且减少了对象之间的紧耦合。 总的来说,Spring框架的依赖注入是通过配置文件或注解的方式来管理对象之间的依赖关系,框架会自动将需要注入的属性注入到对象中。这样可以简化开发过程,提高代码的可维护性和可测试性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值