当前位置: 首页 > news >正文

东莞网站视频北仑网站制作

东莞网站视频,北仑网站制作,包头住房和城乡建设厅网站,网站建设需要多少钱?依赖注入#xff08;Dependency Injection#xff0c;简称DI#xff09;是一种设计模式#xff0c;用于解耦组件#xff08;服务#xff09;之间的依赖关系。它通过将依赖关系的创建和管理交给外部容器来实现#xff0c;而不是在组件#xff08;服务#xff09;内部直…依赖注入Dependency Injection简称DI是一种设计模式用于解耦组件服务之间的依赖关系。它通过将依赖关系的创建和管理交给外部容器来实现而不是在组件服务内部直接创建依赖对象。 ​ 咱就是通过 IServiceCollection 和 IServiceProvider 来实现的他们直接被收入到了runtime libraries在整个.NET平台下通用 3.1 ServiceCollection ​ IServiceCollection 本质是一个 ServiceDescriptor 而 ServiceDescriptor 则是用于描述服务类型实现和生命周期 public interface IServiceCollection : IListServiceDescriptor,ICollectionServiceDescriptor,IEnumerableServiceDescriptor,IEnumerable; 官方提供一些列拓展帮助我们向服务容器中添加服务描述具体在 ServiceCollectionServiceExtensions builder.Services.AddTransientStudentService(); builder.Services.AddKeyedTransientIStudentRepository, StudentRepository(a); builder.Services.AddKeyedTransientIStudentRepository, StudentRepository2(b); builder.Services.AddTransientTransientService(); builder.Services.AddScopedScopeService(); builder.Services.AddSingletonSingletonService(); 3.2 ServiceProvider ​ IServiceProvider 定义了一个方法 GetService帮助我们通过给定的服务类型获取其服务实例 public interface IServiceProvider {object? GetService(Type serviceType); } ​ 下面是 GetService 的默认实现如果不给定engine scope则默认是root public object? GetService(Type serviceType) GetService(ServiceIdentifier.FromServiceType(serviceType), Root); 也就是 internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope) {if (_disposed){ThrowHelper.ThrowObjectDisposedException();}// 获取服务标识符对应的服务访问器ServiceAccessor serviceAccessor _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);// 执行解析时的hockOnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);// 通过服务访问器提供的解析服务得到服务实例object? result serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));return result; } ​ 其中服务标识符 ServiceIdentifier 其实就是包了一下服务类型和服务Key为了.NET8的键化服务 internal readonly struct ServiceIdentifier : IEquatableServiceIdentifier {public object? ServiceKey { get; }public Type ServiceType { get; } } ​ 显而易见的我们的服务解析是由 serviceAccessor.RealizedService 提供而创建服务访问器 serviceAccessor 只有一个实现 CreateServiceAccessor private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier) {// 通过 CallSiteFactory 获取服务的调用点CallSite这是服务解析的一个表示形式。ServiceCallSite? callSite CallSiteFactory.GetCallSite(serviceIdentifier, new CallSiteChain());// 如果调用站点不为空则继续构建服务访问器。if (callSite ! null){DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);// 触发创建调用站点的相关事件。OnCreate(callSite);// 如果调用站点的缓存位置是根Root即表示这是一个单例服务。if (callSite.Cache.Location CallSiteResultCacheLocation.Root){// 直接拿缓存内容object? value CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);return new ServiceAccessor { CallSite callSite, RealizedService scope value };}// 通过引擎解析FuncServiceProviderEngineScope, object? realizedService _engine.RealizeService(callSite);return new ServiceAccessor { CallSite callSite, RealizedService realizedService };}// 如果调用点为空则它的实现服务函数总是返回 null。return new ServiceAccessor { CallSite callSite, RealizedService _ null }; } 3.2.1 ServiceProviderEngine ​ ServiceProviderEngine 是服务商解析服务的执行引擎它在服务商被初始化时建立。有两种引擎分别是动态引擎和运行时引擎在 NETFRAMEWORK || NETSTANDARD2_0 默认使用动态引擎。 private ServiceProviderEngine GetEngine(){ServiceProviderEngine engine;#if NETFRAMEWORK || NETSTANDARD2_0engine CreateDynamicEngine(); #elseif (RuntimeFeature.IsDynamicCodeCompiled !DisableDynamicEngine){engine CreateDynamicEngine();}else{// Dont try to compile Expressions/IL if they are going to get interpretedengine RuntimeServiceProviderEngine.Instance;} #endifreturn engine;[UnconditionalSuppressMessage(AotAnalysis, IL3050:RequiresDynamicCode,Justification CreateDynamicEngine wont be called when using NativeAOT.)] // see also https://github.com/dotnet/linker/issues/2715ServiceProviderEngine CreateDynamicEngine() new DynamicServiceProviderEngine(this);} 由于.NET Aot技术与dynamic技术冲突因此Aot下只能使用运行时引擎但动态引擎在大多情况下仍然是默认的。 动态引擎使用了emit技术这是一个动态编译技术而aot的所有代码都需要在部署前编译好因此运行时无法生成新的代码。那运行时引擎主要使用反射目标是在不牺牲太多性能的情况下提供一个在aot环境中可行的解决方案。 ​ 我们展开动态引擎来看看它是如何解析服务的。 public override FuncServiceProviderEngineScope, object? RealizeService(ServiceCallSite callSite) {// 定义一个局部变量来跟踪委托被调用的次数int callCount 0;return scope {// 当委托被调用时先使用CallSiteRuntimeResolver.Instance.Resolve方法来解析服务。这是一个同步操作确保在编译优化之前服务可以被正常解析。var result CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);// 委托第二次被调用通过UnsafeQueueUserWorkItem在后台线程上启动编译优化if (Interlocked.Increment(ref callCount) 2){// 将一个工作项排队到线程池但不捕获当前的执行上下文。_ ThreadPool.UnsafeQueueUserWorkItem(_ {try{// 用编译优化后的委托替换当前的服务访问器主要用到emit/expression技术_serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));}catch (Exception ex){DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex, _serviceProvider.GetHashCode());Debug.Fail($We should never get exceptions from the background compilation.{Environment.NewLine}{ex});}},null);}return result;}; } 这个实现的关键思想是第一次解析服务时使用一个简单的运行时解析器这样可以快速返回服务实例。然后当服务再被解析它会在后台线程上启动一个编译过程生成一个更高效的服务解析委托。一旦编译完成新的委托会替换掉原来的委托以后的服务解析将使用这个新的、更高效的委托。这种方法可以在不影响应用程序启动时间的情况下逐渐优化服务解析的性能。 3.2.2 ServiceProviderEngineScope ​ ServiceProviderEngineScope 闪亮登场他是我们服务商的代言人从定义不难看出他对外提供了服务商所具备的一切能力 internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IKeyedServiceProvider, IAsyncDisposable, IServiceScopeFactory {// this scope中所有实现IDisposable or IAsyncDisposable的服务private Listobject? _disposables;// 解析过的服务缓存其实就是scope生命周期的服务缓存singleton生命周期的服务缓存都直接挂在调用点上了internal DictionaryServiceCacheKey, object? ResolvedServices { get; }// 实锤服务商代言人public IServiceProvider ServiceProvider this;// 没错啦通过root scope我们又能继续创建无数个scope他们彼此独立public IServiceScope CreateScope() RootProvider.CreateScope(); } 我们来观察他获取服务的逻辑可以发现他就是很朴实无华的用着我们根服务商 ServiceProvider去解析服务那 engine scope 呢就是 this。现在我们已经隐约可以知道engine scope就是为了满足scope生命周期而生。而 ResolvedServices 中存的呢就是该scope中的所有scope生命周期服务实例啦在这个scope中他们是唯一的。 public object? GetService(Type serviceType) {if (_disposed){ThrowHelper.ThrowObjectDisposedException();}return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this); } 再来看另一个核心的方法CaptureDisposable实现disposable的服务会被添加到 _disposables。 internal object? CaptureDisposable(object? service) {// 如果服务没有实现 IDisposable or IAsyncDisposable那么不需要捕获直接原路返回if (ReferenceEquals(this, service) || !(service is IDisposable || service is IAsyncDisposable)){return service;}bool disposed false;lock (Sync){if (_disposed) // 如果scope已经销毁则进入销毁流程{disposed true;}else{_disposables ?? new Listobject();_disposables.Add(service);}}// Dont run customer code under the lockif (disposed) // 这表示我们在试图捕获可销毁服务时scope就已经被销毁{if (service is IDisposable disposable){disposable.Dispose();}else{// sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool.object? localService service; // copy to avoid closure on other pathsTask.Run(() ((IAsyncDisposable)localService).DisposeAsync().AsTask()).GetAwaiter().GetResult();}// 这种case会抛出一个ObjectDisposedExceptionThrowHelper.ThrowObjectDisposedException();}return service; } 在engine scope销毁时其作用域中所有scope生命周期且实现了disposable的服务其实就是_disposable呢也会被一同的销毁。 public ValueTask DisposeAsync() {Listobject? toDispose BeginDispose(); // 获取_disposableif (toDispose ! null){// 从后往前依次销毁服务} } ​ 那么有同学可能就要问了单例实例既然不存在root scope中而是单独丢到了调用点上那他是咋销毁的压根没看到啊那不得泄露了 ​ 其实呀同学们并不用担心这个问题。首先单例服务的实例确实是缓存在调用点上但 disable 服务仍然会被 scope 捕获呀在下文会详细介绍。在 BeginDispose 中的我们会去判断如果是 singleton case且root scope 没有被销毁过我们会主动去销毁喔~ if (IsRootScope !RootProvider.IsDisposed()) RootProvider.Dispose();3.3 ServiceCallSite ​ ServiceCallSite 的主要职责是封装服务解析的逻辑它可以代表一个构造函数调用、属性注入、工厂方法调用等。DI系统使用这个抽象来表示服务的各种解析策略并且可以通过它来生成服务实例。 internal abstract class ServiceCallSite {protected ServiceCallSite(ResultCache cache){Cache cache;}public abstract Type ServiceType { get; }public abstract Type? ImplementationType { get; }public abstract CallSiteKind Kind { get; }public ResultCache Cache { get; }public object? Value { get; set; }public object? Key { get; set; }public bool CaptureDisposable ImplementationType null ||typeof(IDisposable).IsAssignableFrom(ImplementationType) ||typeof(IAsyncDisposable).IsAssignableFrom(ImplementationType); } 3.3.1 ResultCache ​ 其中 ResultCache 定义了我们如何缓存解析后的结果 public CallSiteResultCacheLocation Location { get; set; } // 缓存位置 public ServiceCacheKey Key { get; set; } // 服务key键化服务用的 ​  CallSiteResultCacheLocation 是一个枚举定义了几个值 Root表示服务实例应该在根级别的 IServiceProvider 中缓存。这通常意味着服务实例是单例的Singleton在整个应用程序的生命周期内只会创建一次并且在所有请求中共享。 Scope表示服务实例应该在当前作用域Scope中缓存。对于作用域服务Scoped实例会在每个作用域中创建一次并在该作用域内的所有请求中共享。 Dispose尽管这个名称可能会让人误解但在 ResultCache 的上下文中Dispose 表示着服务是瞬态的每次请求都创建新实例。 None表示没有缓存服务实例。 ​ ServiceCacheKey 结构体就是包了一下服务标识符和一个slot用于适配多实现的 internal readonly struct ServiceCacheKey : IEquatableServiceCacheKey {public ServiceIdentifier ServiceIdentifier { get; }public int Slot { get; } // 那最后一个实现的slot是0 } 3.3.2 CallSiteFactory.GetCallSite ​ 那我们来看看调用点是怎么创建的吧其实上面已经出现过一次了 private ServiceCallSite? CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain) {if (!_stackGuard.TryEnterOnCurrentStack()) // 防止栈溢出{return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceIdentifier, callSiteChain);}// 获取服务标识符对应的锁以确保在创建调用点时的线程安全。// 是为了保证并行解析下的调用点也只会被创建一次例如// C - D - A// E - D - Avar callsiteLock _callSiteLocks.GetOrAdd(serviceIdentifier, static _ new object());lock (callsiteLock){// 检查当前服务标识符是否会导致循环依赖callSiteChain.CheckCircularDependency(serviceIdentifier);// 首先尝试创建精确匹配的服务调用站点如果失败则尝试创建开放泛型服务调用站点如果还是失败则尝试创建枚举服务调用站点。如果所有尝试都失败了callSite将为null。ServiceCallSite? callSite TryCreateExact(serviceIdentifier, callSiteChain) ??TryCreateOpenGeneric(serviceIdentifier, callSiteChain) ??TryCreateEnumerable(serviceIdentifier, callSiteChain);return callSite;} } 那服务点的创建过程我就简单概述一下啦 查找调用点缓存存在就直接返回啦 服务标识符会被转成服务描述符 ServiceDescriptor key化服务不指定key默认取last 计算ServiceCallSite依次是 TryCreateExact 计算 ResultCache 如果已经有实现实例了则返回 ConstantCallSite表示直接返回已经创建的实例的调用点。 如果有实现工厂则返回 FactoryCallSite表示通过工厂方法创建服务实例的调用点。 如果有实现类型则返回 ConstructorCallSite表示通过构造函数创建服务实例的调用点。 TryCreateOpenGeneric 根据泛型定义获取服务描述符 ServiceDescriptor 计算 ResultCache 使用服务标识符中的具体泛型参数来构造实现的闭合类型 AOT兼容性测试因为不能保证值类型泛型的代码已经生成 如果成功闭合则返回 ConstructorCallSite表示通过构造函数创建服务实例的调用点。 TryCreateEnumerable 确定类型是 IEnumerableT AOT兼容性测试因为不能保证值类型数组的代码已经生成 如果 T 不是泛型类型并且可以找到对应的服务描述符集合则循环 TryCreateExact 否则方向循环 TryCreateExact然后方向循环 TryCreateOpenGeneric 3.4 CallSiteVisitor ​ 好了有了上面的了解我们可以开始探索服务解析的内幕了。服务解析说白了就是引擎围着 CallSiteVisitor 转圈圈所谓的解析服务其实就是访问调用点了。 protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument) {if (!_stackGuard.TryEnterOnCurrentStack()) // 一些校验分栈啥的{return _stackGuard.RunOnEmptyStack(VisitCallSite, callSite, argument);}switch (callSite.Cache.Location){case CallSiteResultCacheLocation.Root: // 单例return VisitRootCache(callSite, argument);case CallSiteResultCacheLocation.Scope: // 作用域return VisitScopeCache(callSite, argument);case CallSiteResultCacheLocation.Dispose: // 瞬态return VisitDisposeCache(callSite, argument);case CallSiteResultCacheLocation.None: // 不缓存ConstantCallSitereturn VisitNoCache(callSite, argument);default:throw new ArgumentOutOfRangeException();} } 为了方便展示我们这里的解析器都拿运行时来说因为内部是反射而emit、expression实在是难以观看。 ​ 3.4.1 VisitRootCache 那我们来看看单例的情况下是如何访问的 protected override object? VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context) {if (callSite.Value is object value){// Value already calculated, return it directlyreturn value;}var lockType RuntimeResolverLock.Root;// 单例都是直接放根作用域的ServiceProviderEngineScope serviceProviderEngine context.Scope.RootProvider.Root;lock (callSite){// 这里搞了个双检锁来确保在多线程环境中同一时间只有一个线程可以执行接下来的代码块。// Lock the callsite and check if another thread already cached the valueif (callSite.Value is object callSiteValue){return callSiteValue;}object? resolved VisitCallSiteMain(callSite, new RuntimeResolverContext{Scope serviceProviderEngine,AcquiredLocks context.AcquiredLocks | lockType});// 捕获可销毁的服务serviceProviderEngine.CaptureDisposable(resolved);// 缓存解析结果到调用点上callSite.Value resolved;return resolved;} } 好可以看到真正解析调用点的主角出来了 VisitCallSiteMain那这里的 CallSiteKind 上面计算 ServiceCallSite 时呢已经讲的很清楚啦咱对号入座就行了 protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) {switch (callSite.Kind){case CallSiteKind.Factory:return VisitFactory((FactoryCallSite)callSite, argument);case CallSiteKind.IEnumerable:return VisitIEnumerable((IEnumerableCallSite)callSite, argument);case CallSiteKind.Constructor:return VisitConstructor((ConstructorCallSite)callSite, argument);case CallSiteKind.Constant:return VisitConstant((ConstantCallSite)callSite, argument);case CallSiteKind.ServiceProvider:return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);default:throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType()));} } 我们就看看最经典的通过构造函数创建服务实例的调用点 ConstructorCallSite很显然就是new嘛只不过可能构造中依赖其它服务那就递归创建呗。easy其它几种太简单了大家自己去看看吧。 protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) {object?[] parameterValues;if (constructorCallSite.ParameterCallSites.Length 0){parameterValues Array.Emptyobject();}else{parameterValues new object?[constructorCallSite.ParameterCallSites.Length];for (int index 0; index parameterValues.Length; index){// 递归构建依赖的服务parameterValues[index] VisitCallSite(constructorCallSite.ParameterCallSites[index], context);}}// new (xxx)return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameterValues, culture: null); } 3.4.2 VisitScopeCache 在访问单例缓存的时候呢仅仅通过了一个double check lock就搞定了因为人家全局的嘛咱再来看看访问作用域缓存会不会有什么不一样 protected override object? VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context) {// Check if we are in the situation where scoped service was promoted to singleton// and we need to lock the rootreturn context.Scope.IsRootScope ?VisitRootCache(callSite, context) :VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope); } 哈哈它果然很不一般啊上来就来检查我们是否是 root scope。如果是这种case呢则走 VisitRootCache。但是奇怪啊为什么访问 scope cache所在 engine scope 能是 root scope ​ 还记得 ServiceProvider 获取的服务实例的核心方法吗engine scope 他是传进来的如果我们给一个 root scope自然就出现的这种case只是这种 case 特别罕见。 internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope) ​  VisitCache 的同步模型写的实在是酷我们看 RuntimeResolverLock 的枚举就两个Scope 1 和 Root 2 AcquiredLocksScope时 那 AcquiredLocksfalse0 显然成立申请锁也就是尝试独占改作用域的ResolvedServices 申请成功进入同步块重新计算AcquiredLocks|true1 如此在该engine scope 中这条链路上的调用点都被占有直到结束 AcquiredLocksRoot 时 那显然 engine scope 也应该是 root scope那么走 VisitRootCache case 在 VisitRootCache 通过DCL锁住 root scope 上链路涉及的服务点直至结束 ​至此我们应该不难看出这个设计的精妙之处即在非 root scopescope生命周期中scope之间是互相隔离的没有必要像 root scopesingleton生命周期那样在所有scope中独占服务点。 private object? VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine {bool lockTaken false;object sync serviceProviderEngine.Sync;DictionaryServiceCacheKey, object? resolvedServices serviceProviderEngine.ResolvedServices;if ((context.AcquiredLocks lockType) 0){Monitor.Enter(sync, ref lockTaken);}try{// Note: This method has already taken lock by the caller for resolution and access synchronization.// For scoped: takes a dictionary as both a resolution lock and a dictionary access lock.if (resolvedServices.TryGetValue(callSite.Cache.Key, out object? resolved)){return resolved;}// scope服务的解析结果是放在engine scope的ResolvedServices上的而非调用点resolved VisitCallSiteMain(callSite, new RuntimeResolverContext{Scope serviceProviderEngine,AcquiredLocks context.AcquiredLocks | lockType});serviceProviderEngine.CaptureDisposable(resolved);resolvedServices.Add(callSite.Cache.Key, resolved);return resolved;}finally{if (lockTaken){Monitor.Exit(sync);}} } 3.4.3 VisitDisposeCache ​ 我们看最后一个也就是 Transient case protected override object? VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context) {return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context)); } ​ 异常的简单我们沿用了scope的设计但是我们没有进行任何缓存行为。即每次都去访问调用点。 文章转载自xiaolipro 原文链接https://www.cnblogs.com/xiaolipro/p/17873575.html
http://www.hkea.cn/news/14309000/

相关文章:

  • 网络科技有限公司网站建设策划书北京网络营销岗位数量
  • 建设银行网站特色信息流推广渠道
  • 深圳个人网站制作比汉斯设计网站素材
  • 做网站大概需要几个人怎么查名字有没有被注册商标
  • 网站乱码为网站网站做网络维护
  • 书画展示网站模板手机搞笑网站模板下载安装
  • 好的文化网站模板下载wordpress不能发文章_只能在标题内写字
  • 做聚划算网站增城网站建设方案
  • 保定市做网站的公司外包人力资源公司
  • 做网站用的产品展示横幅网站建设的维护与更新
  • 马鞍山做网站网页图片大全
  • 内部门户网站建设方案wordpress 花园
  • 可以不使用备案的网站名吗张店区网站建设特点有哪些 谢谢
  • 德惠市建设局网站苏州做网站0512jinyan
  • 襄阳网站建设公司哪家好南宁哪个网络公司建网站好
  • 网站建站系统程序美术生最吃香的专业
  • 做用户名验证的网站服务器外贸个人网站
  • 深圳网站建设公司首选未来的软件开发方向是什么
  • 网站开发实战asp制作视频教程ueditor wordpress html被转义
  • 达州市住房和建设厅网站vultr wordpress
  • 国外网站需要备案吗广州公司网站建设
  • 大连零基础网站建设培训中心佳木斯城乡建设局官方网站
  • 口碑好的购物网站建设wordpress装主题失败
  • WordPress网站hym地图株洲专业网站排名优化
  • 哪些网站做黑名单象山关键词seo排名
  • 网站建设工作内容投票网站怎么制作
  • 无障碍网站建设方案惠州建设局网站
  • 漳州做网站简书 wordpress
  • 设计企业网站哪家好南通网站seo报价
  • 我的世界查询建筑网站网站内做营销活动使用工具