• angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。


    最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇。

    如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么?

    这篇博客其实是angular源码阅读之路的一个必经站点,就是要理解injector,provider,module之间的关系——这关系其实就是依赖注入的本质。

    那么请专注地看下面这一段话吧:

    通俗一点的理解:

    module是发布任务的BOSS。

    injector是领取任务的中间人。

    provider是真正去执行任务的马仔。

    当然上面这一段话只是比喻,不太严谨,可是很形象。待我慢慢解释来。

    如果你比较熟悉angular,那么你肯定知道在每一个module对象上,都有一个私有属性"_invokeQueue"。

    这个_invokeQueue,其实就是module发布的任务。

    怎么理解『_invokeQueue,其实就是module发布的任务。』这句话呢?请看下面的简单小代码。

    当我执行下面这段语句,我会在myapp中创建一个全局变量name='不咬人的蚊子':

    //注册了一个全局变量name='不咬人的蚊子'
    angular.module('myapp').constant('name','不咬人的蚊子');
    

    而这个变量'name'我可以在controller里面这样使用:

    angular.module.controller('myctr',['$scope','name',function($scope,name){
        console.log(name)//不咬人的蚊子
        $scope.name  = name;
    }])
    

    现在说回_invokeQueue,当我执行了那个注册全局变量的constant方法的时候,其实是module发布了一个任务,这个任务保存在_invokeQueue里面。

    注意:其实这时候只是发布任务,任务并没有被执行。这时候_invokeQueue里面是这样的:

    module._invokeQueue=[
        ['constant',['name','不咬人的蚊子']]//数组里面包含着另一个数组。
    ]
    

    对,没错,这就是Module发布的任务,invokeQueue其实就是一个数组,里面有着一系列任务(这里只是拿constant举例,其实在真实案例中,还会有各种任务,比如controller啊什么的)。

    invokeQueue这个数组里面的每一个元素都是一个任务,如你所见,这任务也是一个数组。

    任务数组的第1个元素(下标为0)记录了这个任务具体是什么任务,是constant,还是controller,还是directive等等。

    任务数组的第2个元素(下标为1)记录了执行任务需要的参数。

    注意注意,这里我们为了易于理解,只拿constant举例子,以后慢慢复杂起来,会越来越丰富。

    注意注意,module发布了任务以后,只是发布了,并没有执行。

    那么什么时候执行呢?

    当angular一个app启动的时候,会自动生成一个injector,也就是大家口中的注射器,这是一个对象,这个injector对象会读取module中的各种任务。

    比如injecotr读取module的invokeQueue之后,发现了第一条任务:

     ['constant',['name','不咬人的蚊子']]
    

    于是injector就会发现,这是一个constant任务,参数是name,'不咬人的蚊子'。

    injector并不能处理constant任务,所以它去找一个名为constant的provider,这个provider可以提供一个函数,这个函数正好接收两个参数。

    于是injector把任务中的两个参数(也就是name和'不咬人的蚊子'这两个参数)交给constantProvider,让它来执行。

    好了,这就是一个口头能讲明白的原理。那么angularJs是如何实现这个机制的呢?我打算把简单版的代码贴在下面,如果感兴趣的同学可以看看,如果不感兴趣的同学其实只要把上面的文字给看明白了,下面的代码随便看个乐呵就行。(这个代码可能会有部分是接着上一篇博客的代码,如果看着不知道怎么回事,可以看看上一篇博客。) 

    setupModuleLoader.js

    function setupModuleLoader(window){
    	var ensure=function(obj,name,factory){
    		return obj[name]||(obj[name]=factory())
    	}
    	var angular = ensure(window,'angular',Object);
    
    	var createModule = function(name,requires){
    		var invokeQueue=[];//增加一个任务队列
    		var moduleInstance = {
    			name:name,
    			requires:requires,
    			_invokeQueue:invokeQueue,
                            //constant方法的实质是向invokeQueue数组里面增加一个任务
    			constant:function(key,value){
    				invokeQueue.push(['constant',[key,value]])
    			},
    		};
    		return moduleInstance;
    	}
    
    	ensure(angular,'module',function(){
    		var modules={};
    		return function(name,requires){
    			if(requires){
    				return createModule(name,requires,modules)
    			}else{
    				return getModule(name,modules);
    			}
    		}
    	})
    }
    

    createInjector.js

    //createInjector(['app1','app2'])
    //参数是一个字符串或者一个数组,内容是module名
    function createInjector(modulesToLoad){
    	//cache用来缓存一些一直可以用到的值
    	var cache={};
    
    	$provide={
    		constant:function(key,value){
    			cache[key]=value;
    		}
    	}
    
    	//这里的foreach方法并不是一个真正能运行的foreach,能看懂就行了
    	//每次APP启动的时候,injector都会按照传入的module名来遍历所有module
    	//这样就可以得到所有module发布的任务,并且一一执行这些任务
    	$.forEach(modulesToLoad,function(moduleName){
    		var module = window.angular.module(moduleName);
    		$.forEach(module._invokeQueue,function(invokeArgs){
    			var method=invokeArgs[0];
    			var args = invokeArgs[1];
    			$provide[method].apply($provide,args);
    		})
    	})
    	return {
    		has:function(key){
    			return cache.hasOwnProperty(key)
    		},
    		get:function(key){
    			return cache[key]
    		}
    	}
    }
    

    如果你耐着性子看到了这里,并且思路还算清晰,那么你肯定会问,现在injector执行了所有任务,并且把一切东西都准备好了,那么我们使用这些数据的途径和方法是什么呢?哈哈,这个别急,很快会解释明白,但是现在起码我们对依赖注入有了一个很好的理解了不是么?冬天来了,春天不会远了。

      

     

      

  • 相关阅读:
    VMware中Ubuntu 14.04出现Unknown Display问题解决
    VMWare桥接、NAT和only-host三种模式
    Tomcat目录下文件详解
    Java socket2
    Java socket1
    网络基础知识
    java awt 乱码问题
    窗口Dialog
    windowsEvents
    鼠标适配器Adapter
  • 原文地址:https://www.cnblogs.com/oukichi/p/5982189.html
Copyright © 2020-2023  润新知