.Net Core后端架构实战【介入IOC控制反转】

news2024/11/28 18:57:07

引言

    Inversion of Control,简称IOC,即控制反转。记得当初刚实习的时候公司的带我的人和我提到过IOC这个概念,当初完全不知道是
啥东西。后来有幸写了半年Java,SpringBoot里面业务开发随处可见IOC。再后来我写.Net Core用到的第一个框架Blog.Core项目,它里
面IRepository与Repository和IServices与Services,这种解耦的程度单说它贯彻依赖倒置原则是非常nice的!.NetCore时代更加注
重面向接口编程,并且它内置轻量型的IOC容器可帮助我们灵活的进行开发。

依赖注入

Dependency Injection,何为依赖注入?由容器动态的将对象依赖的资源注入到对象之中。假设容器在构造对象A时,对象A的构造依赖对象B、对象C、对象D这些参数,容器会将这些依赖关系自动构造好,最大限度的完成程序的解耦。依赖注入(Dependency Injection,简称DI)就是构造A对象时,需要依赖B对象,那么就先构造B对象作为参数传递到A对象,这种对象初始化并注入的技术就叫做依赖注入。

.NetCore本身就集成了一个轻量型的IOC容器,我们可以把程序中的抽象(Interface)或业务类根据需要的生命周期配置到容器中。他不是一次把所有的实例都构造出来,当你在用的时候他才会去构造实例!.NetCore内置的IOC容器只支持构造函数注入!

如下三种生命周期的注入:

Singleton:在第一次被请求的时候被创建,对象的构造函数只会执行一次然后一直会存在内存中之后就一直使用这一个实例。如果在多线程中使用了Singleton,要考虑线程安全的问题,保证它不会有冲突。

Scoped:一个请求只创建这一个实例,请求会被当成一个范围,也就是说单个请求对象的构造函数只会执行一次,同一次请求内注入多次也用的是同一个对象。假如一个请求会穿透应用层、领域层、基础设施层等等各层,在穿层的过程中肯定会有同一个类的多次注入,那这些多次注入在这个作用域下维持的就是单例。例如工作单元模式就可以借助这种模式的生命周期来实现,像EF Core的DbContext上下文就被注册为作用域服务。

Transient:每次请求时都会创建一个新的实例,只要注入一次,构造函数就会执行一次即实例化一次。Scoped、Transient的区别是你在同一个上下文中是否期望使用同一个实例,如果是,用Scoped,反之则使用Transient。

😊接下来我们来看下.Net Core中相关IOC核心源码😊

IOC源码

ServiceDescriptor

这个类是用来描述服务注册时的服务类型、实例类型、实现类型、生命周期等信息。就如同他本身的描述:Describes a service with its service type, implementation, and lifetime.       

DI容器ServiceProvider之所以能够根据我们给定的服务类型(一般是一个接口类型)提供一个能够开箱即用的服务实例,是因为我们预先注册了相应的服务描述信息,就是说ServiceDescriptor为容器提供了正确的服务信息以供容器选择对应的实例。

这个类一共有四个构造函数,本质其实就是给描述具体注册的Lifetime、ServiceType、ImplementationType、ImplementationInstance、ImplementationFactory赋值。请看源码:

这里的话只列举其中的一个构造函数,因为我发现在文章中源码不能罗列的太多,不然让人看得嫌烦😅

下面这个构造函数需要两个参数,分别是服务元素的类型,服务元素实例,默认是单例的,其它的几个构造函数都与其很相仿。

/// <summary>
/// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="instance"/>
/// as a <see cref="ServiceLifetime.Singleton"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="instance">The instance implementing the service.</param>
public ServiceDescriptor(
    Type serviceType,
    object instance)
    : this(serviceType, ServiceLifetime.Singleton)
{
    if (serviceType == null)
    {
        throw new ArgumentNullException(nameof(serviceType));
    }
    if (instance == null)
    {
        throw new ArgumentNullException(nameof(instance));
    }
    ImplementationInstance = instance;
}

 我们再来看下它里面几个重要的函数:

Describe()函数,可以看到这个函数也是用来生成ServiceDescriptor类的,它们分别对应上面的构造函数,也就是说为实例化ServiceDescriptor提供另外一种方式。源码如下:

private static ServiceDescriptor Describe<TService, TImplementation>(ServiceLifetime lifetime)
    where TService : class
    where TImplementation : class, TService
{
    return Describe(
        typeof(TService),
        typeof(TImplementation),
        lifetime: lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationType"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Describe(Type serviceType, Type implementationType, ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, implementationType, lifetime);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <param name="lifetime">The lifetime of the service.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime)
{
    return new ServiceDescriptor(serviceType, implementationFactory, lifetime);
}

Singleton()函数,这个函数一共有七个重载,我只贴出其中三个方法,其它几个都差不多,总之就是一句话用于生成单例化的ServiceDescriptor类。源码如下:

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton<TService, TImplementation>()
    where TService : class
    where TImplementation : class, TService
{
    return Describe<TService, TImplementation>(ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton(Type service, Type implementationType)
{
    if (service == null)
    {
        throw new ArgumentNullException(nameof(service));
    }
    if (implementationType == null)
    {
        throw new ArgumentNullException(nameof(implementationType));
    }
    return Describe(service, implementationType, ServiceLifetime.Singleton);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <param name="implementationInstance">The instance of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Singleton(
    Type serviceType,
    object implementationInstance)
{
    if (serviceType == null)
    {
        throw new ArgumentNullException(nameof(serviceType));
    }
    if (implementationInstance == null)
    {
        throw new ArgumentNullException(nameof(implementationInstance));
    }
    return new ServiceDescriptor(serviceType, implementationInstance);
}

Scoped()函数,这个函数有五个重载,其实他内部还是调用Describe()方法来生成作用域的ServiceDescriptor类。源码如下:

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TImplementation">The type of the implementation.</typeparam>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped<TService, TImplementation>()
    where TService : class
    where TImplementation : class, TService
{
    return Describe<TService, TImplementation>(ServiceLifetime.Scoped);
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/> and <paramref name="implementationType"/>
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="service">The type of the service.</param>
/// <param name="implementationType">The type of the implementation.</param>
/// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
public static ServiceDescriptor Scoped(Type service, Type implementationType)
{
    return Describe(service, implementationType, ServiceLifetime.Scoped);
}

通过Scoped()方法我们可以这样注册:

services.Add(ServiceDescriptor.Scoped<IPersonService, PersonService>());
//services.Add(ServiceDescriptor.Scoped(typeof(IPersonService),typeof(PersonService)));
//或
services.Add(ServiceDescriptor.Transient<IPersonService, PersonService>());
//services.Add(ServiceDescriptor.Transient(typeof(IPersonService), typeof(P

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1055525.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用DMA的触发循环实现eTMR的PWM周期计数

利用DMA的触发循环实现对eTMR的PWM周期计数 文章目录 利用DMA的触发循环实现对eTMR的PWM周期计数引言分析问题eTMR的调试模式ModulizationFTM的多次重载事件终极大招-使用触发链 解决问题确认DMAMUX中的eTMR相关触发源eTMR产生触发信号 软件总结参考文献 引言 最近在同客户一起…

朝气蓬勃 后生可畏

介绍: 线段树是一棵二叉搜索树,思想与分治很想,把一段区间平分平分再平分,平分到不能平分为止,可以进行方便的区间修改和区间查询,当然,树状数组能做的单点修改、单点查询,线段树也可以更好地实现,总之,线段树是树状数组的升级版,此外,线段树能做的平衡树也能做,但…

Acwing 839. 模拟堆

Acwing 839. 模拟堆 题目描述思路讲解代码展示 题目描述 思路讲解 代码展示 #include <iostream> #include <algorithm> #include <string.h>using namespace std;const int N 100010;int h[N], ph[N], hp[N], cnt;void heap_swap(int a, int b) {swap(ph[…

2021-06-17 51蛋骗鸡0-99自动加减

缘由怎么实现两位秒表自动加减_嵌入式-CSDN问答 #include "REG52.h" sbit K1 P1^5; sbit K2 P1^6; sbit K3 P1^7; sbit K4 P1^4; //sbit LED1P1^0; //sbit LED2P1^1; //sbit LED3P1^2; //sbit LED4P1^3; bit k0; unsigned char code SmZiFu[]{63,6,91,79,102,10…

CentOS安装OpenNebula(一)

OpenNebula是一个非常成熟的云平台&#xff0c;&#xff0c;十分简单&#xff0c;但是功能十分丰富。它提供了十分灵活的解决方案&#xff0c;让用户能建立并管理企业云和虚拟的数据中心。 OpenNebula是专门为云计算打造的开源系统&#xff0c;用户可以使用Xen、KVM、VMware等…

JAVA 异常分类及处理

1 概念 如果某个方法不能按照正常的途径完成任务&#xff0c;就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时&#xff0c;这个方法会立刻退出同时不返回任何值。另外&#xff0c;调用 这个方法的其他代码也无法继续执行&#xff0c;异常处理…

controller-manager学习三部曲之一:通过脚本文件寻找程序入口

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 关于《controller-manager学习三部曲》 《controller-manager学习三部曲》是欣宸原创的kubernetes深入学习系列之一&#xff0c;在前面的《client-go实战》系…

Pikachu靶场——目录遍历漏洞和敏感信息泄露

文章目录 1. 目录遍历漏洞1.1 源码分析1.2 漏洞防御 2. 敏感信息泄露2.1 漏洞防御 1. 目录遍历漏洞 漏洞描述 目录遍历漏洞发生在应用程序未能正确限制用户输入的情况下。攻击者可以利用这个漏洞&#xff0c;通过在请求中使用特殊的文件路径字符&#xff08;如 …/ 或 %2e%2e…

Acwing 838. 堆排序

Acwing 838. 堆排序 题目描述思路讲解代码展示 题目描述 思路讲解 堆是一颗完全二叉树&#xff0c;除了最下面一层&#xff0c;其余是满的&#xff0c;最后一层从左到右排列 小根堆&#xff1a;每个点小于等于左右两堆&#xff0c;所以根节点就是最小值 大根堆&#xff1a;每个…

2021-06-18 51蛋骗鸡实现流水灯累积点亮全亮后闪烁从头开始循环

缘由怎么在单片机上实现这个功能呢_有问必答-CSDN问答 #include "REG52.h" sbit K1 P1^5; sbit K2 P1^6; sbit K3 P1^7; sbit K4 P1^4; //sbit LED1P1^0; //sbit LED2P1^1; //sbit LED3P1^2; //sbit LED4P1^3; bit k1; unsigned char code SmZiFu[]{63,6,91,79,…

【U8+】用友U8建立账套,提示准备模板文件出错。

【问题描述】 用友U8在新建账套的过程中&#xff0c; 初始化环境&#xff0c;提示准备模板文件出错&#xff0c; SQL Server 阻止了对组件xp_cmdshell的过程sys.xp cmdshell的访问&#xff0c;因为此组件已作为此服务器安全配置的一部分而被关闭。 【解决方法】 出现这种错误…

【中秋国庆不断更】HarmonyOS对通知类消息的管理与发布通知(下)

一、发布进度条类型通知 进度条通知也是常见的通知类型&#xff0c;主要应用于文件下载、事务处理进度显示。HarmonyOS提供了进度条模板&#xff0c;发布通知应用设置好进度条模板的属性值&#xff0c;如模板名、模板数据&#xff0c;通过通知子系统发送到通知栏显示。 目前系统…

Java基础---第十一篇

系列文章目录 文章目录 系列文章目录一、说说List,Set,Map三者的区别?二、Object 有哪些常用方法?大致说一下每个方法的含义三、Java 创建对象有几种方式?一、说说List,Set,Map三者的区别? List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象…

28272-2012 米制超细牙螺纹 系列和基本尺寸

声明 本文是学习GB-T 28272-2012 米制超细牙螺纹 系列和基本尺寸. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了米制超细牙螺纹的基本牙型、直径与螺距系列和基本尺寸。 本标准适用于精密仪器和电子设备等领域的螺纹连接。 2…

Linux软硬连接与动静态库

硬链接 我们可以通过命令 ln myproc myproc-h 创建硬链接。 很容易发现 test&#xff08;源文件&#xff09; 和 mytest &#xff08;硬链接&#xff09;的文件大小是一样的&#xff0c;mytest的硬链接数从 1-> 2 通过 ls -i 文件名 我们还会发现&#xff0c;test 和 myte…

10以内字符与数字的转化(与ASCII码无关)

字符转整型数字 char a3; int ba1; cout<<b; 这样打印出的结果是51&#xff1b;显然我们想打印出的结果是4&#xff1b; 那么下面介绍一种极其简单的方法&#xff1b; 1.利用“ -0 ” char a6; int ba-01; cout<<b; 输出结果就是7&#xff1b; 不过这种方…

Vue--Axios详解

一、Axios是什么&#xff1f; Axios是一种异步请求技术&#xff0c;核心作用就是用来给页面 发送异步请求的&#xff0c;并获取对应数据在页面中的渲染&#xff0c;是页面局部更新的Ajax封装来的。 特性 从浏览器创建 XMLHttpRequests从 node.js 创建 http 请求支持 Promise A…

【Vue3】兄弟组件传参

1. 借助父组件传参 A 组件派发一个事件&#xff0c;修改 flag 的值&#xff0c;先传递给父组件&#xff0c;然后由父组件传递给 B 组件。 缺点&#xff1a;必须由 App.vue 处理中间逻辑。 A.vue <template><div class"A"><h1>A组件</h1>…

Tomcat8 任意写文件PUT方法 (CVE-2017-12615)

Tomcat 任意写文件PUT方法 (CVE-2017-12615) 文章目录 Tomcat 任意写文件PUT方法 (CVE-2017-12615)1 在线漏洞解读:2 版本影响3 环境搭建4 漏洞复现4.1 访问4.2 POC攻击点4.2.1 直接发送以下数据包&#xff0c;然后shell将被写入Web根目录。4.2.2 访问是否通&#xff0c;可以访…