小王的故事

小王去上班

​ 小王是个程序员,每个工作日他都要去上班,诸多交通工具他最喜欢的交通工具是骑电车。在骑行的过程中放空自己使他很快。

​ 突然有一天天气预报说近期有很大的雨,小王再想骑电车去上班显然是不可能了。那么他就要选择开汽车去。

但是由于小王每天过于依赖骑电动车,开汽车水平有限,那么他就要重新学习开汽车。

​ 因此小王很烦恼,我就想去上个班,还要掌握不同的交通工具,这真是让人烦恼,难道我就想做一个单纯的打工族就这么难吗?小王就把这件事告诉了他的老婆,她老婆是个老司机,她说“这事你就不用管了,我是老司机啊,开车这事我来控制就好了,你就记得给我多买些礼物就好“。从此之后小王就安心的为了老婆的赚钱,开车这事就完全有他老婆负责。

小王的日记:

​ 我曾经很享受自己去上班(自己控制),每天去上班就拿出我心爱的电动车(实例化对象),直到有一天天气预报告诉我要下大雨,再依赖自行车就会被大雨淋着,而换交通工具我发现我就要重新学习开车(高耦合),知道我老婆大人说她是老司机(ioc容器),开车这事由她来控制(控制反转),不管怎么去上班,事先告诉他用什么交通工具就行(依赖注入),从此我每个工作日只要叫上我老婆她就直接带我去上班了。


依赖注入

​ 从小王的故事我们可以看到一些关键词依赖控制 而今天要说的就是什么是依赖注入。说到依赖注入(DI)还有个概念叫控制反转(IOC)。

控制反转(IOC—Inversion of Control)不是什么技术,而是一种设计思想。它的思路是设计好的依赖类交给容器控制,而不是在对象中直接实例化控制。就是在系统运行时ioc容器动态向一个对象提供他依赖的其他对象。而他的实现就是用依赖注入来实现。

依赖注入(DI—Dependency Injection)是组件之间依赖关系由容器决定。为此我们要明白依赖和注入关系

依赖:由于应用程序需要ioc容器提供对象外部的资源所以应用程序依赖于ioc容器

注入:某个对象所需要的外部资源(包括对象、资源、常量数据)注入到了ioc容器中。


net5 内置依赖注入

依赖注入是net core和net5的核心概念,我们在了解完概念之后也要对我们框架中对他的实现有个清楚的认识。当然官方文档是最好的了解方式。接下来我从我的角度去说一下对net 5(net core)依赖注入的理解.首先我们要知道net5依赖注入的实现由net 内置的容器和第三方的容器。我们主要说内置的实现情况。

内置依赖注入

Microsoft.Extensions.DependencyInjection

net5 内置依赖注入主要用到了Microsoft.Extensions.DependencyInjectionMicrosoft.Extensions.DependencyInjection.Abstraction两个引用。我们nuget引用就行。它们是开源的。



我们可以看到DependencyInjection这个项目并不大,但却是整个net5(NET Core)的基础,因为它提供了依赖注入容器的默认实现,而依赖注入是net5(net core)的核心基础.

源码解析

IServiceCollection

public class ServiceCollection : IServiceCollection
{
//ServiceDescriptor缓存集合
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
//注册到当前ServiceCollection对象中的ServiceDescriptor数量
public int Count => _descriptors.Count;
public bool IsReadOnly => false;
public ServiceDescriptor this[int index]
{
get
{
return _descriptors[index];
}
set
{
_descriptors[index] = value;
}
}
//清空所有ServiceDescriptor对象
public void Clear()
{
_descriptors.Clear();
}
//查询ServiceCollection是否包含指定ServiceDescriptor对象
public bool Contains(ServiceDescriptor item)=> _descriptors.Contains(item);
//拷贝ServiceDescriptor
public void CopyTo(ServiceDescriptor[] array, int arrayIndex) =>_descriptors.CopyTo(array, arrayIndex);
//从ServiceCollection移除指定ServiceDescriptor
public bool Remove(ServiceDescriptor item)=>_descriptors.Remove(item);
//获取此ServiceCollection的迭代器
public IEnumerator<ServiceDescriptor> GetEnumerator()=> _descriptors.GetEnumerator();
public int IndexOf(ServiceDescriptor item) => _descriptors.IndexOf(item);
public void Insert(int index, ServiceDescriptor item) => _descriptors.Insert(index, item);
public void RemoveAt(int index)=> _descriptors.RemoveAt(index);
}

IServiceCollection&&ServiceDescriptor

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// 指定服务描述符集合的约定
/// </summary>
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
}

我们从代码中可以看到内置依赖注入,它的内置容器是ServiceProvider,我们会把我们的服务注入到ServiceProvider 中来,而IServiceCollection是ServiceProvider的list的集合。

ServiceDescriptor

     public static IServiceCollection Add(this IServiceCollection collection,IEnumerable<ServiceDescriptor> descriptors)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
if (descriptors == null)
{
throw new ArgumentNullException(nameof(descriptors));
}
foreach (var descriptor in descriptors)
{
collection.Add(descriptor);
}
return collection;
}

我们可以看到服务注册的时候,提供了ServiceDescriptor

 /// <summary>
/// Describes a service with its service type, implementation, and lifetime.
/// </summary>
[DebuggerDisplay("Lifetime = {Lifetime}, ServiceType = {ServiceType}, ImplementationType = {ImplementationType}")]
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; }

构造函数

//派生类型
public ServiceDescriptor(Type serviceType,object instance)
: this(serviceType, ServiceLifetime.Singleton)
{
Lifetime = lifetime;
ServiceType = serviceType;
ImplementationInstance = instance;
}
//工厂
public ServiceDescriptor(Type serviceType,Func<IServiceProvider, object> factory,ServiceLifetime lifetime)
: this(serviceType, lifetime)
{
Lifetime = lifetime;
ServiceType = serviceType;
ImplementationFactory = factory;
}
//具体实例对象
public ServiceDescriptor(Type serviceType,Type implementationType,ServiceLifetime lifetime)
: this(serviceType, lifetime)
{
Lifetime = lifetime;
ServiceType = serviceType;
ImplementationType = implementationType;
}
/// <summary>
///获取当前注册类型的实例类型(内部类)
/// </summary>
/// <returns></returns>
internal Type GetImplementationType(){}
//真正实例化对象的方法,重载都是调用此类方法
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime){}
public static ServiceDescriptor Singleton(Type serviceType,object implementationInstance){}
public static ServiceDescriptor Scoped(Type service, Func<IServiceProvider, object> implementationFactory){}

ServiceCollectionServiceExtensions&ServiceCollectionDescriptorExtensions&ServiceCollectionContainerBuilderExtensions

这三个方法时ServiceCollection的三个扩展方法分别实现:我们所使用了注册方式;TryAdd和RemoveAll,Replace等操作和构造ServiceProvider实例。

ServiceCollectionServiceExtensions:

代码太多(不同生命周期的注册)就不贴出了,截个图算了。



ServiceCollectionDescriptorExtensions:

![image-20210515122419417](https://gitee.com/wyl1924/cdn/raw/master/img/blog/image-20210515122419417.png

public static void TryAdd(this IServiceCollection collection,ServiceDescriptor descriptor)
{
if (!collection.Any(d => d.ServiceType == descriptor.ServiceType))
collection.Add(descriptor);
}

注:Add,RemoveAll,Replace,没什么好说的,其中TryAdd,TryAddEnumerable需要注意到的是:中有服务集合中不存在才会进行过注册。

ServiceCollectionContainerBuilderExtensions

    public static class ServiceCollectionContainerBuilderExtensions
{
public static ServiceProvider BuildServiceProvider(this IServiceCollection services)
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) }

IServiceProvider

public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
{
// ServiceProvider的扩展接口
// 使用这个接口的子类进行调用缓存各种注册服务和调用访问者对象进行获取实例对象
private readonly IServiceProviderEngine _engine;
/// 缓存类型(访问者模式)
private readonly CallSiteValidator _callSiteValidator;
internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
{
IServiceProviderEngineCallback callback = null;
if (options.ValidateScopes)
{
callback = this;
_callSiteValidator = new CallSiteValidator();
}
//实例化工作引擎类型
switch (options.Mode)
{
case ServiceProviderMode.Dynamic:
// 实例化 DynamicServiceProviderEngine
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
break;
case ServiceProviderMode.Runtime:
_engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
break;
case ServiceProviderMode.ILEmit:
_engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
break;
case ServiceProviderMode.Expressions:
_engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
break;
default:
throw new NotSupportedException(nameof(options.Mode));
}
}
// 获取指定类型的服务对象
public object GetService(Type serviceType) => _engine.GetService(serviceType);
public void Dispose() => _engine.Dispose();
//创建服务实例缓存
void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
=>_callSiteValidator.ValidateCallSite(callSite);
//服务实例的校验
void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
=>_callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
}

我们可以看到ServiceProvider用来获取服务的实例,它提供了一个扩展IServiceProviderEngine来实现其功能。x下面我们来看一下源码

public static class ServiceProviderServiceExtensions
{
// 从IServiceProvider获取“T”类型的服务。
public static T GetService<T>(this IServiceProvider provider)
=> (T)provider.GetService(typeof(T)); // 从IServiceProvider获取“T”类型的服务。
public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
{
// 如果当前ServiceProvider实现了 ISupportRequiredService
// 则直接调用当前ServiceProvier的GetRequiredService获取服务实例
var requiredServiceSupportingProvider = provider as ISupportRequiredService;
if (requiredServiceSupportingProvider != null)
return requiredServiceSupportingProvider.GetRequiredService(serviceType);
//如果当前ServiceProvider未实现ISupportRequiredService
//就直接调用GetService获取服务实例,但是如果服务实例为空,则抛出异常
var service = provider.GetService(serviceType);
if (service == null)
throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
return service;
}
public static T GetRequiredService<T>(this IServiceProvider provider)
=> (T)provider.GetRequiredService(typeof(T));
//获取指定注册类型<T>的所有服务实例
public static IEnumerable<T> GetServices<T>(this IServiceProvider provider)
=> provider.GetRequiredService<IEnumerable<T>>();
//获取指定注册类型<T>的所有服务实例
public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType)
{
//制造一个serviceType类型的IEnumberable<>集合,serviceTypele类型作为当前集合的泛型参数
var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
} //创建一个子IServiceProvider(容器)实例
public static IServiceScope CreateScope(this IServiceProvider provider)
=> provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

NET 内置依赖注入实现

我们再Startup.ConfigureServices(IServiceCollection services)进行注册下面我直接用代码来看一下实现

public class UserService : IUserService
{
public string GetName()
{
return "王延领";
}
}
public interface IUserService
{
string GetName();
}

1.面向接口注册

services.AddScoped(typeof(IUserService), typeof(UserService));
services.AddScoped<IUserService, UserService>();

2.实现形式注册

services.AddScoped<UserService>();
services.AddScoped(typeof(UserService));

两种形式都可以,但扩展性显然是第一种好。

3.批量注入

var assembly = Assembly.GetExecutingAssembly()
.DefinedTypes
.Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I"));
foreach (var item in assembly)
{
services.AddScoped(item.GetInterfaces().FirstOrDefault(), item);
}

第三方容器(Autofact)注入

后续单独一篇文章再写吧。

net5学习笔记---依赖注入的更多相关文章

  1. Spring 学习笔记 ----依赖注入

    依赖注入 有三种方式,本文只学习下属性注入. 属性注入       属性注入即通过 setXxx方法()注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入方式是 ...

  2. Spring学习笔记--依赖注入

    依赖注入和控制反转:http://baitai.iteye.com/blog/792980出自李刚<轻量级 Java EE 企业应用实战> Java应用是一种典型的依赖型应用,它就是由一些 ...

  3. DotNETCore 学习笔记 依赖注入和多环境

    Dependency Injection ------------------------------------------------------------------------ ASP.NE ...

  4. ADO学习笔记之注入漏洞与参数化查询

    ADO学习笔记之注入漏洞与参数化查询 作为新手,在学习ADO程序时,使用 sql 语言查询数据时,很容易写类似如下代码: using (SqlConnection con = new SqlConne ...

  5. Web安全学习笔记 SQL注入下

    Web安全学习笔记 SQL注入下 繁枝插云欣 --ICML8 SQL注入小技巧 CheatSheet 预编译 参考文章 一点心得 一.SQL注入小技巧 1. 宽字节注入 一般程序员用gbk编码做开发的 ...

  6. Web安全学习笔记 SQL注入上

    Web安全学习笔记 SQL注入上 繁枝插云欣 --ICML8 SQL注入分类 SQL注入检测 一.注入分类 1.简介 SQL注入是一种代码注入技术用于攻击数据驱动的应用程序在应用程序中,如果没有做恰当 ...

  7. Web安全学习笔记 SQL注入中

    Web安全学习笔记 SQL注入中 繁枝插云欣 --ICML8 权限提升 数据库检测 绕过技巧 一.权限提升 1. UDF提权 UDF User Defined Function,用户自定义函数 是My ...

  8. 学习Spring——依赖注入

    前言: 又开始动笔开了“学习Spring”系列的头…… 其实一开始写“学习SpringMVC”的几篇文章是出于想系统的了解下Spring以及SpringMVC,因为平时在公司中虽然每天都在使用Spri ...

  9. Spring框架学习之依赖注入

    Spring框架从2004年发布的第一个版本以来,如今已经迭代到5.x,逐渐成为JavaEE开发中必不可少的框架之一,也有人称它为Java下的第一开源平台.单从Spring的本身来说,它贯穿着整个表现 ...

随机推荐

  1. DAOS 分布式异步对象存储|故障模型

    DAOS 依靠大规模分布式单端口存储.因此,每个 Target 实际上都是一个单独的失败点. DAOS 通过在不同的容错域中提供 Target 间的冗余来实现数据和元数据的可用性和持久性.DAOS 内 ...

  2. [SpringCloud教程]2. 版本选型和项目搭建

    Spring Cloud Alibaba 版本选型 建议先选择Spring Cloud Alibaba的大版本,方便兼容 选择 Spring Cloud Alibaba 大版本 访问链接,找到标题&q ...

  3. JS实现环绕地球飞行的3D飞行线动画效果(JS+HTML)

    1.项目介绍 JS+HTML实现绕地球飞行的3D飞行线动画效果,且3D地球可以随意拖动和滑动缩放,画面中心是蓝色地球,地球表面上的两点连线之间有光电随机出现沿着抛物线轨迹3D飞行,可使用较好的浏览器打 ...

  4. Python 多线程(一)

    Python多线程(一) 目录 Python多线程(一) 线程 Python线程模型 Python虚拟机按照下面所述方式来切换线程 Threading模块 未引进线程 结果 引入线程 方式一:创建Th ...

  5. ret2dl32

    ret2dl32 首先检查一下保护: IDA分析一下 程序很简单就是,往bss段上的buf读入0x400个数据,然后拷贝到栈上.read_got还被置为0,这一看就是要逼着你使用ret2dlresol ...

  6. Day01_07_Java关键字和字面值

    关键字 https://www.cnblogs.com/chenglc/p/6922834.html 字面值(所见即所得的数据) 10 100 3.14 'a' "abc" tur ...

  7. Day05_23_封装

    封装 什么是封装? 封装可以被认为是一个类的保护屏障,防止该类的代码和数据被外部类定义的代码随机访问.要访问该类的代码和数据,必须通过严格的接口控制.封装最主要的功能在于我们能修改自己的实现代 码,而 ...

  8. 【WPF】将控件事件中的参数,传递到ViewModel中

    在MVVM模式下,在通常使用命令(Command)绑定的方式的时候 ,使用的是 CommandParameter 属性进行参数的传递. 但是很多时候,有一些事件我们需要使用其中的一些事件里面的参数,以 ...

  9. Review: JQuery

    1.DOM access with jQuery 1 $("h1"); //select all the h1s 2 $("#heading"); // sel ...

  10. IDEA中集成Git

    一.新建项目,绑定GIT 1.新建spring boot项目 2.路径选择git本地文件地址 3.新的项目文件绑定git,将远程的git文件拷贝至项目中  二.修改文件,使用IDEA操作GIT 1.提 ...