net5学习笔记---依赖注入
小王的故事
小王去上班
小王是个程序员,每个工作日他都要去上班,诸多交通工具他最喜欢的交通工具是骑电车。在骑行的过程中放空自己使他很快。
突然有一天天气预报说近期有很大的雨,小王再想骑电车去上班显然是不可能了。那么他就要选择开汽车去。
但是由于小王每天过于依赖骑电动车,开汽车水平有限,那么他就要重新学习开汽车。
因此小王很烦恼,我就想去上个班,还要掌握不同的交通工具,这真是让人烦恼,难道我就想做一个单纯的打工族就这么难吗?小王就把这件事告诉了他的老婆,她老婆是个老司机,她说“这事你就不用管了,我是老司机啊,开车这事我来控制就好了,你就记得给我多买些礼物就好“。从此之后小王就安心的为了老婆的赚钱,开车这事就完全有他老婆负责。小王的日记:
我曾经很享受自己去上班(自己控制),每天去上班就拿出我心爱的电动车(实例化对象),直到有一天天气预报告诉我要下大雨,再依赖自行车就会被大雨淋着,而换交通工具我发现我就要重新学习开车(高耦合),知道我老婆大人说她是老司机(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.DependencyInjection
和Microsoft.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:

{
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学习笔记---依赖注入的更多相关文章
- Spring 学习笔记 ----依赖注入
依赖注入 有三种方式,本文只学习下属性注入. 属性注入 属性注入即通过 setXxx方法()注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入方式是 ...
- Spring学习笔记--依赖注入
依赖注入和控制反转:http://baitai.iteye.com/blog/792980出自李刚<轻量级 Java EE 企业应用实战> Java应用是一种典型的依赖型应用,它就是由一些 ...
- DotNETCore 学习笔记 依赖注入和多环境
Dependency Injection ------------------------------------------------------------------------ ASP.NE ...
- ADO学习笔记之注入漏洞与参数化查询
ADO学习笔记之注入漏洞与参数化查询 作为新手,在学习ADO程序时,使用 sql 语言查询数据时,很容易写类似如下代码: using (SqlConnection con = new SqlConne ...
- Web安全学习笔记 SQL注入下
Web安全学习笔记 SQL注入下 繁枝插云欣 --ICML8 SQL注入小技巧 CheatSheet 预编译 参考文章 一点心得 一.SQL注入小技巧 1. 宽字节注入 一般程序员用gbk编码做开发的 ...
- Web安全学习笔记 SQL注入上
Web安全学习笔记 SQL注入上 繁枝插云欣 --ICML8 SQL注入分类 SQL注入检测 一.注入分类 1.简介 SQL注入是一种代码注入技术用于攻击数据驱动的应用程序在应用程序中,如果没有做恰当 ...
- Web安全学习笔记 SQL注入中
Web安全学习笔记 SQL注入中 繁枝插云欣 --ICML8 权限提升 数据库检测 绕过技巧 一.权限提升 1. UDF提权 UDF User Defined Function,用户自定义函数 是My ...
- 学习Spring——依赖注入
前言: 又开始动笔开了“学习Spring”系列的头…… 其实一开始写“学习SpringMVC”的几篇文章是出于想系统的了解下Spring以及SpringMVC,因为平时在公司中虽然每天都在使用Spri ...
- Spring框架学习之依赖注入
Spring框架从2004年发布的第一个版本以来,如今已经迭代到5.x,逐渐成为JavaEE开发中必不可少的框架之一,也有人称它为Java下的第一开源平台.单从Spring的本身来说,它贯穿着整个表现 ...
随机推荐
- HDU_3949 XOR 【线性基】
一.题目 XOR 二.分析 给定$N$个数,问它的任意子集异或产生的数进行排列,求第K小的数. 构造出线性基$B$后,如果$|B| < N$,那么代表N个数中有一个数是可以由线性基中的其他数异或 ...
- 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求
用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...
- std::thread线程库详解(5)
目录 目录 前言 信号量 counting_semaphore latch与barrier latch barrier 总结 前言 前面四部分内容已经把目前常用的C++标准库中线程库的一些同步库介绍完 ...
- 使用Amazon Pinpoint对用户行为追踪
1.前言 最近在做一个项目,我们的后台大数据团队需要了解用户在使用app的时候,都进行了哪些操作,在哪个页面都干了些什么,以及app日活和月活等等,各种数据.总之就是监控用户行为,说好听一点就是发送反 ...
- [SpringCloud教程]2. 版本选型和项目搭建
Spring Cloud Alibaba 版本选型 建议先选择Spring Cloud Alibaba的大版本,方便兼容 选择 Spring Cloud Alibaba 大版本 访问链接,找到标题&q ...
- 构建之法与CI/CD
项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 个人阅读作业2 我在这个课程的目标是 认识软工,拥抱软工,提升相关能力以便日后与其朝夕相伴 这个作业在哪个具 ...
- Java(265-278)【Map】
1.Map集合概述 是一个接口 键是唯一的 java.util.Map<k,v>集合 Map集合的特点: 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个val ...
- Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(五):鉴权 gRPC-Interceptor 拦截器实战
拦截器(gRPC-Interceptor)类似于 Gin 中间件(Middleware),让你在真正调用 RPC 服务前,进行身份认证.参数校验.限流等通用操作. 系列 云原生 API 网关,gRPC ...
- 消息中间件rabbitMQ
1 为什么使用消息队列啊? 其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么 面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这 ...
- Ambassador-09-prefix正则表达式
设置 prefix_regex: true,即prefix就可以设置成正则表达式 --- apiVersion: getambassador.io/v2 kind: Mapping metadata: ...