• angular源码分析2-依赖注入实现


    场景实例

    如上代码,run方法依赖注入testervice服务,app模块加载执行run方法的传入的函数时,注入testService实例化的单例对象,在函数执行时,获取获取/设置该对象的属性值,或执行该对象的属性方法。在执行run方法传递的函数时,打印输出a。

    下面先看看数据的流动过程,然后分析代码的实现。在js中皆为数据,function也是数据类型的一种。在这里,function和其他数据类型一样,也理解为数据类型。

    数据流动

    上面的js文件加载后,通过调用angular.module('app')方法获取模块app的模块对象,调用run方法把function(testProvider){console.log (testProvider.a);})放入模块的对象的_runBlocks队列中,然后返回模块对象。调用模块对象的service方法把['$provide','provider',Arguments(2)]放入模块对象的_invokeQueue队列中,其中Argument(2)为调用第8行的 service传入的参数的argumens。

    modules.app._runBlocks = [... ,function(testProvider){console.log (testProvider.a);}), ...]

    modules.app._invokeQueue = [... ,['$provide','provider',Arguments(2)], ...]

    在加载模块的过程中, 会执行modules.app._invokeQueue队列和执行modules.app._runBlocks队列中的函数。

    执行完modules.app._invokeQueue后,会把队列的元素转化处理缓存存储在providerCache对象中,

    providerCache.testProviderProvider = {
        $get:function(){
            return {a:'a'};
        }
    }

    在执行modules.app._runBlocks队列中的函数时,会执行如下函数:

    function(testProvider){
        console.log(testProvider.a);
    }

    结果输出a。

    在执行上面的函数时,先获取testProvider服务,从provideCache.testProviderProvider获得{a:'a'}对象,并且存储在instanceCache对象中,即

    instanceCache.testProvider = {
        a : 'a'
    }

    执行上面函数时,通过apply方法把testProvider对象作为实参传入,函数内部可操作该对象。若下次需依赖注入 testProvider,直接从instanceCache获取该对象。

    
    

    代码分析

    第3875行instanceCache对象用于存储与依赖注入有关的信息。用于实例化服务单例对象和缓存服务对象。第3976行到3980行创建了$injector服务,并且挂载在instanceCache对象上,即instanceCache.$injector。利用闭包特性,服务$injector可以访问第3977行createInternalInjector传的实参。下面看一下,是如何创建$injector服务的。

    第4114行到第4135行createInternalInjector函数用于创建$injector服务。在第3977行到第3980行调用该函数后,返回从第4126行到第4134行包含的对象,这个对象即$injector服务。其中invoke函数用于执行使用依赖注入的函数和获取依赖服务的对象,如function(testProvider){}依赖testProvider的函数。get函数用于根据函数的参数的名字获取相应的服务对象,如该对象没有被实例化过,实例化对象并缓存在instanceCache对象中,并返回实例化后的对象。如果依赖注入的对象实例化过,直接从缓存中对象获取并返回。annotate函数返回函数所依赖的依赖服务的形参名组成的数组。

    第4114行到第4136行的getService用于根据服务名获取服务实例。

    第4138行到第4164行的invoke函数用于执行依赖注入的函数。如上面的函数function(testProvider){}。先获取testProvider服务,然后把testProvider服务的单例对象,作为该函数的实参传入,并执行这个函数。第 4144行获取fn的依赖注入的服务名字,即['testProvider']。第4147行到4157行用于获取函数fn依赖的服务。第4163行把依赖的服务传入函数,并且执行该函数。

    providerCache与instanceCache 

    providerCache缓存对象:

    providerCache = {
        $provide: {
                provider: supportObject(provider),
                factory: supportObject(factory),
                service: supportObject(service),
                value: supportObject(value),
                constant: supportObject(constant),
                decorator: decorator
              },
        $injector:{},
    
        //模块加载时,缓存的一些经过处理后的服务,不是最终的服务对象,
        //生成的最终对象缓存在instanceCache对象中
        testProviderProvider:{
           $get: function (){
                {a : 'a'}
           }
       },
       ...
    }

    instanceCache对象:

    instanceCache = {
        $injector: {
             invoke: invoke,
             instantiate: instantiate,
             get: getService,
             annotate: annotate,
             has: function(name) {
                 return providerCache.hasOwnProperty(name + 
                    providerSuffix) || cache.hasOwnProperty(name);
             }
        },
       
        //缓存实例化对象
        testProvider: {
            a : 'a'
        }
        ...
    }

    providerCache对象与instanceCache对象贯穿于整个框架,是框架的核心数据。

    providerCache对象主要用于模块加载时,操作和缓存注入的provider,service ,factory等服务。

    instanceCache对象借助于providerCache对象实现依赖服务的管理。实例化服务,并缓存服务单例对象。

  • 相关阅读:
    Laravel框架中的event事件操作
    PHP魔术方法实例
    PHP 面向对象
    ThinkPHP中where()使用方法详解
    PHP常见错误提示含义解释
    php面向对象编程self和static的区别
    php文件路径获取文件名
    php三种无限分类
    php高精度计算问题
    转:JavaScript定时机制、以及浏览器渲染机制 浅谈
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/7011418.html
Copyright © 2020-2023  润新知