[Asp.net 5] DependencyInjection项目代码分析3-Ninject

Microsoft.Framework.DependencyInjection.Ninject

该工程内部共包含5个类文件,底层使用Ninject实现依赖注入,工程截图如下:

从文件命名可以看出,NinjectServiceProvider和NinjectServiceScopeFactory分别是接口IServiceProvider和IServiceScopeFactory的实现类。

(IServiceScope接口的实现类作为NinjectServiceScopeFactory内部类而存在,没有作为单独的文件。)

而工程的入口依旧是Registration为结尾的NinjectRegistration类。

NinjectRegistration

文件代码如下

 public static class NinjectRegistration
    {
        public static void Populate(this IKernel kernel, IEnumerable<ServiceDescriptor> descriptors)
        {
            kernel.Load(new ServiceProviderNinjectModule(descriptors));
        }

        public static IBindingNamedWithOrOnSyntax<T> InRequestScope<T>(
                this IBindingWhenInNamedWithOrOnSyntax<T> binding)
        {
            return binding.InScope(context => context.Parameters.GetScopeParameter());
        }

        internal static ScopeParameter GetScopeParameter(this IEnumerable<IParameter> parameters)
        {
            return (ScopeParameter)(parameters
                .Where(p => p.Name == typeof(ScopeParameter).FullName)
                .SingleOrDefault());
        }

        internal static IEnumerable<IParameter> AddOrReplaceScopeParameter(
                this IEnumerable<IParameter> parameters,
                ScopeParameter scopeParameter)
        {
            return parameters
                .Where(p => p.Name != typeof(ScopeParameter).FullName)
                .Concat(new[] { scopeParameter });
        }
    }

静态方法:

Populate:作用是将ServiceProviderNinjectModule注册到内核中。对于Ninject大多数时候都会将自定义继承自NinjectModule的类注册到内核中,并且使用该继承类管理依赖注入。

GetScopeParameter:获取所有“ScopeParameter”类型的参数

AddOrReplaceScopeParameter:获取所有“ScopeParameter”参数,并且将它替换成传入的“scopeParameter”。

InRequestScope:在“binding.InScope”中添加“ScopeParameter”。

总体说来,该文件都是些辅助方法,也是对外初始化的接口。

ServiceProviderNinjectModule

文件代码如下:

internal class ServiceProviderNinjectModule : NinjectModule
    {
        private readonly IEnumerable<ServiceDescriptor> _serviceDescriptors;

        public ServiceProviderNinjectModule(
                IEnumerable<ServiceDescriptor> serviceDescriptors)
        {
            _serviceDescriptors = serviceDescriptors;
        }

        public override void Load()
        {
            foreach (var descriptor in _serviceDescriptors)
            {
                IBindingWhenInNamedWithOrOnSyntax<object> binding;

                if (descriptor.ImplementationType != null)
                {
                    binding = Bind(descriptor.ServiceType).To(descriptor.ImplementationType);
                }
                else if (descriptor.ImplementationFactory != null)
                {
                    binding = Bind(descriptor.ServiceType).ToMethod(context =>
                    {
                        var serviceProvider = context.Kernel.Get<IServiceProvider>();
                        return descriptor.ImplementationFactory(serviceProvider);
                    });
                }
                else
                {
                    binding = Bind(descriptor.ServiceType).ToConstant(descriptor.ImplementationInstance);
                }

                switch (descriptor.Lifetime)
                {
                    case ServiceLifetime.Singleton:
                        binding.InSingletonScope();
                        break;
                    case ServiceLifetime.Scoped:
                        binding.InRequestScope();
                        break;
                    case ServiceLifetime.Transient:
                        binding.InTransientScope();
                        break;
                }
            }

            Bind<IServiceProvider>().ToMethod(context =>
            {
                var resolver = context.Kernel.Get<IResolutionRoot>();
                var inheritedParams = context.Parameters.Where(p => p.ShouldInherit);

                var scopeParam = new ScopeParameter();
                inheritedParams = inheritedParams.AddOrReplaceScopeParameter(scopeParam);

                return new NinjectServiceProvider(resolver, inheritedParams.ToArray());
            }).InRequestScope();

            Bind<IServiceScopeFactory>().ToMethod(context =>
            {
                return new NinjectServiceScopeFactory(context);
            }).InRequestScope();
        }
    }

虽然代码量相对多一些,不过内部逻辑也很简单。

首先是按照惯例,将表示依赖注入关系的IEnumerable<ServiceDescriptor>类型参数传入。

之后重载Load方法,在内核调用时根据IEnumerable<ServiceDescriptor>初始化依赖注入关系。

Load方法,首先遍历IEnumerable<ServiceDescriptor> 对于每一个ServiceDescriptor对象,按照实现类类型、实现类工厂、实现类实例的顺序注入,之后设置相应的生命周期。最后注入IServiceProvider为NinjectServiceProvider、IServiceScopeFactory为NinjectServiceScopeFactory类型。

*需要注意的是NinjectServiceProvider和NinjectServiceScopeFactory注入的范围是Scope,并且对于每个NinjectServiceProvider都会替换其scopeParam参数,以保证其Scope范围。

NinjectServiceProvider

internal class NinjectServiceProvider : IServiceProvider
    {
        private static readonly MethodInfo _getAll;

        private readonly IResolutionRoot _resolver;
        private readonly IParameter[] _inheritedParameters;
        private readonly object[] _getAllParameters;

        static NinjectServiceProvider()
        {
            _getAll = typeof(ResolutionExtensions).GetMethod(
                "GetAll", new Type[] { typeof(IResolutionRoot), typeof(IParameter[]) });
        }

        public NinjectServiceProvider(IResolutionRoot resolver, IParameter[] inheritedParameters)
        {
            _resolver = resolver;
            _inheritedParameters = inheritedParameters;
            _getAllParameters = new object[] { resolver, inheritedParameters };
        }

        public object GetService(Type type)
        {
            return GetSingleService(type) ??
                GetLast(GetAll(type)) ??
                GetMultiService(type);
        }

        private object GetSingleService(Type type);
        private IEnumerable GetMultiService(Type collectionType);
        private IEnumerable GetAll(Type type);
        private static object GetLast(IEnumerable services);
    }

该类首先静态构造函数在类文件加载时,进行初始化,将范性扩展方法“IEnumerable<T> GetAll<T>(this IResolutionRoot root, params IParameter[] parameters)” 赋值给内部变量“_getAll”。之后类构造函数初始化,该构造函数将IResolutionRoot _resolver赋值(依赖注入的内部的核心控制类),IParameter[] _inheritedParameters赋值,并且为“_getAll”的参数初始化_getAllParameters。

当系统获取实现类实例时,系统调用GetService(Type)方法。

  • 系统首先调用GetSingleService[内部为_resolver.TryGet(type, _inheritedParameters)],获取简单类型的实现。
  • 如果简单类型为空,则获取将泛型类型的数据。将GetAll方法的T类型换成实际的类型,之后获取所有该类型的实例,最后去所有实例中最后一个。
private IEnumerable GetAll(Type type)
        {
            var getAll = _getAll.MakeGenericMethod(type);
            return (IEnumerable)getAll.Invoke(null, _getAllParameters);
        }
  • 如果获取所有该类型实例仍然为空,则该Type类型可能为IEnumerable<T>类型,则获取T类型,调用GetAll方法。

NinjectServiceScopeFactory和NinjectServiceScope

这里俩个类继承自IServiceScopeFactory和IServiceScope。

NinjectServiceScopeFactory仅仅在内部创建了NinjectServiceScope对象,代码如下:

public NinjectServiceScopeFactory(IContext context)
        {
            _resolver = context.Kernel.Get<IResolutionRoot>();
            _inheritedParameters = context.Parameters.Where(p => p.ShouldInherit);
        }

        public IServiceScope CreateScope()
        {
            return new NinjectServiceScope(_resolver, _inheritedParameters);
        }

而在NinjectServiceScope内部,每次都是使用新的ScopeParameter参数构建NinjectServiceProvider实例,以确保每次都开启一个新的Scope。代码如下:

 public NinjectServiceScope(
                IResolutionRoot resolver,
                IEnumerable<IParameter> inheritedParameters)
            {
                _scope = new ScopeParameter();
                inheritedParameters = inheritedParameters.AddOrReplaceScopeParameter(_scope);
                _serviceProvider = new NinjectServiceProvider(resolver, inheritedParameters.ToArray());
            }

*AddOrReplaceScopeParameter当ScopeParameter类型参数存在时,将其替换成_scope,不存在时添加。

时间: 2024-10-07 20:42:00

[Asp.net 5] DependencyInjection项目代码分析3-Ninject的相关文章

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

这个系列已经写了5篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [Asp.net 5] DependencyInjection项目代码分析3-Ninject [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1) [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2) 如果想

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable&lt;&gt;补充)

Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableService与ClosedIEnumerableService抛下没讲,现在我们就将该部分补充完整. 我们回忆ServiceProvider类的构造函数(对外部使用的)中,注册了IEnumerable<>.new OpenIEnumerableService(_table)的关系. public Se

[Asp.net 5] DependencyInjection项目代码分析-目录

微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [Asp.net 5] DependencyInjection项目代码分析3-Ninject [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1) [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2) [Asp.net

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)

这个系列已经写了6篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [Asp.net 5] DependencyInjection项目代码分析3-Ninject [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1) [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2) [As

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(中)

在 DependencyInjection项目代码分析4-微软的实现(上)中介绍了“ServiceTable”.“ServiceEntry”.“IGenericService”.“IService”.“IServiceCallSite”,这篇介绍下“IGenericService."IService"."IServiceCallSite"实现类 GenericService类 做为IGenericService的唯一实现类,该类言简意赅,请看代码: internal

[Asp.net 5] DependencyInjection项目代码分析

最近在研究开源代码,正好发现Asp.net5的源码,下载地址:https://github.com/aspnet. 今天主要讲的是DependencyInjection这部分,抛砖引玉,供大家参考,也欢迎莅临斧正.闲话不多说,下面就代码进行简单分析 项目架构如下: 一共包含DependencyInjection.DependencyInjection.Abstractions.DependencyInjection.Autofac.DependencyInjection.Ninject以及Dep

[Asp.net 5] DependencyInjection项目代码分析2

Microsoft.Framework.DependencyInjection.Autofac源码分析 该工程只有一个代码静态类AutofacRegistration,但是该类有3个扩展方法,以及3个内部类.扩展方法如下: public static class AutofacRegistration { public static void Populate(this ContainerBuilder builder,IEnumerable<ServiceDescriptor> descri

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(上)

前面俩种实现中,很多内部细节都无法知道,微软的框架也是为了屏蔽具体实现,只让我们关注接口.但是人都是充满好奇的,依赖注入到底是怎么实现的呢? 微软又有怎样的实现呢?下面就为大家一一呈现(说实话,代码真不好读) 先看下核心类:ServiceTable internal class ServiceTable { private readonly object _sync = new object(); private readonly Dictionary<Type, ServiceEntry>

结对队友个人项目代码分析

项目要求 项目名称:中小学数学卷子自动生成程序 用户: 小学.初中和高中数学老师. 功能: 1.命令行输入用户名和密码,两者之间用空格隔开(程序预设小学.初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示"当前选择为XX出题",XX为小学.初中和高中三个选项中的一个.否则提示"请输入正确的用户名.密码",重新输入用户名.密码: 2.登录后,系统提示"准备生成XX数学题目,请输入生成题目数量:",XX为小学.初中和高中三