最开始使用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执行了所有任务,并且把一切东西都准备好了,那么我们使用这些数据的途径和方法是什么呢?哈哈,这个别急,很快会解释明白,但是现在起码我们对依赖注入有了一个很好的理解了不是么?冬天来了,春天不会远了。

  

 

  

angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。的更多相关文章

  1. Spring源码剖析依赖注入实现

    Spring源码剖析——依赖注入实现原理 2016年08月06日 09:35:00 阅读数:31760 标签: spring源码bean依赖注入 更多 个人分类: Java   版权声明:本文为博主原 ...

  2. angular源码阅读的起点,setupModuleLoader方法

    angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 ...

  3. angular源码阅读3:真的,依赖注入的原理

    前面已经提到了: 如何注册一个module. 如何获取一个module. injector与module以及provider的关系. 那么已经剩下最后一部分了,就是关于依赖是如何被注入的. 且看下面这 ...

  4. Yii2.0源码阅读-behavior的实现原理

    Yii2.0中的一个思想就是组件化的思想,所以.大多数的类都直接或间接的继承自yii\base\Component,而组件的三大功能:属性.事件.行为. 行为的目的是为了方便的扩展一个类的功能,而不需 ...

  5. ovs源码阅读--流表查询原理

    背景 在ovs交换机中,报文的处理流程可以划分为一下三个步骤:协议解析,表项查找和动作执行,其中最耗时的步骤在于表项查找,往往一个流表中有数目巨大的表项,如何根据数据报文的信息快速的查找到对应的流表项 ...

  6. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  7. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  8. Kubernetes 学习(八)Kubernetes 源码阅读之初级篇------源码及依赖下载

    0. 前言 阅读了一段时间 Golang 开源代码,准备正式阅读 Kubernetes 项目代码(工作机 Golang 版本为 Go 1.12) 参照 <k8s 源码阅读> 选择 1.13 ...

  9. python3 源码阅读-虚拟机运行原理

    阅读源码版本python 3.8.3 参考书籍<<Python源码剖析>> 参考书籍<<Python学习手册 第4版>> 官网文档目录介绍 Doc目录主 ...

随机推荐

  1. JAVASE02-Unit06: 文件操作——File 、 文件操作—— RandomAccessFile

    Unit06: 文件操作--File . 文件操作-- RandomAccessFile java.io.FileFile的每一个实例是用来表示文件系统中的一个文件或目录 package day06; ...

  2. jira任务批量操作示例

    1. 获取jira任务编号 使用字段提取工具获取编号,如WQBNEWSDLDL-347,WQBNEWSDLDL-348: 字段提取正则表达式:(?<2>[\[[](?<1>[A ...

  3. python引用py文件中文报错

    文件 a.py 中引用文件 b.py 如果文件b.py中包含中文,会报错. 文件hello.py中代码如下: def say_nihao(): print "你好" 文件main. ...

  4. [Git] Ubuntu 升级 git 版本

    $ sudo add-apt-repository ppa:git-core/ppa $ sudo apt-get update $ sudo apt-get install git

  5. Hadoop基础知识

    摘要:Hadoop的安装目录了解.etc的核心配置项.hadoop的启动.HDFS文件的block块级副本的存放策略.checkpoint触发设置. 1.hadoop目录了解 bin:可执行文件,命令 ...

  6. post- build event

    Ref:http://blog.csdn.net/teng_ontheway/article/details/8307410 Ref: http://blog.csdn.net/sodickbird/ ...

  7. svg学习(九)path

    <path> 标签用来定义路径. 下面的命令可用于路径数据: M = moveto L = lineto H = horizontal lineto V = vertical lineto ...

  8. U家面试prepare: Serialize and Deserialize Tree With Uncertain Children Nodes

    Like Leetcode 297, Serialize and Deserialize Binary Tree, the only difference, this is not a binary ...

  9. 命令行下Git的使用

    命令行下Git的使用 写在前边的话 以自己即将进行的毕设项目为例,进行Git使用的简易说明.不过由于校园网络的限制,故使用GitOSC. 快速开始 本次git使用位于自己的个人PC上,所以将个人的Gi ...

  10. 打开QQ会话

    Android:String url="mqqwpa://im/chat?chat_type=wpa&uin=123456";startActivity(new Inten ...