angular源码阅读3:真的,依赖注入的原理
前面已经提到了:
如何注册一个module。
如何获取一个module。
injector与module以及provider的关系。
那么已经剩下最后一部分了,就是关于依赖是如何被注入的。
且看下面这段代码。
//如你所见,注册了一个moduleA
//又如你所见,给moduleA上面注册了两个全局变量,a和b
angular.module('moduleA',[]);
angular.module('moduleA').constant('a',1);
angular.module('moduleA').constant('b',2); //生成一个注射器
var injector = createInjector('moduleA'); //定义一个函数,接收两个数作为参数,打印两个数的和
function add(a,b){
console.log(a+b);
}
函数声明好了,moduleA上面也有值了,那么怎么把moduleA上面的值作为函数的参数传进来呢?熟悉angular的小伙伴肯定知道有如下几种办法:
//第一种,给函数加$inject属性
add.$inject=['a','b'];
add(a,b)//3 //第二种,数组形式
['a','b',add(a,b)]; //第三种,直接用
add(a,b);
这次主要看第一种和第二种的原理,第三种的原理有点复杂,留着下一次说。
还记得之前的那个createInjector函数么?这个函数接收一些module名称作为参数,返回一个对象就是injector对象。当createInjector运行的时候,会把所有module都遍历一次,把module的_invokeQueue里面的任务都找到相应的provider执行一下,然后把各个module里面注册的东西都保存一份。
而依赖注入的本质就是——当你执行函数A的时候,根据函数A的$injector属性,或者函数A的数组元素(比如 ['a','b',add(a,b)]这种),去自己保存的那堆东西里面找到相应的数据,把这个数据传给函数进行调用。
来,配上源代码,别看代码长,其实都是上一篇博客的代码,这次只往里面加了一小部分。
//createInjector(['app1','app2'])
//参数是一个字符串或者一个数组,内容是module名
function createInjector(modulesToLoad) {
//cache用来缓存一些一直可以用到的值
var cache = {}; //所有加载过的module都记录在这个对象里面
var loadedModules = {}; $provide = {
constant: function(key, value) {
cache[key] = value;
}
} //这里的foreach方法并不是一个真正能运行的foreach,能看懂就行了
//每次APP启动的时候,injector都会按照传入的module名来遍历所有module
//这样就可以得到所有module发布的任务,并且一一执行这些任务
$.forEach(modulesToLoad, function loadModule(moduleName) {
//查询一下记录,如果没有加载过,再去执行
if (!loadedModules[moduleName]) {
var module = window.angular.module(moduleName);
//每加载一个模块,就操作一下loadedModules,把这个模块信息放进去
loadedModules[moduleName]=true;
//每次先把所有依赖的模块加载进去
$.forEach(module.requires,loadModule);
//然后再去调用这个模块的invokeQueue
$.forEach(module._invokeQueue, function(invokeArgs) {
var method = invokeArgs[0];
var args = invokeArgs[1];
$provide[method].apply($provide, args);
})
}
}) //invoke方法用来调用函数
function invoke(fn){
//把fn.$inject这个数组里面的元素拿出来,args是一个新的数组
var args=$.map(fn.$inject,function(token){
return cache[token];
})
fn.apply(null,args);
} return {
has: function(key) {
return cache.hasOwnProperty(key)
},
get: function(key) {
return cache[key]
},
invoke:invoke
}
}
看到那个invoke方法了么?来先顺一下思路,当createInjector函数加载了moduleA之后,返回了injector的时候,在自己的内部存储的cache里面已经是这样的:
cache={
a:1,
b:2
}
然后给返回的injector增加了一个invoke方法,这个方法是用来调用函数的。代码里看的应该很清楚,invoke方法接收一个函数作为参数,然后去寻找这个函数的$injector属性,这个属性里发现了a,b两个值,于是就去cache里面找key值为a,b的两个值,然后成为一个数组args,在用fn.apply(null,args)调用函数,这样这两个之就成为函数的参数传进去了。
关于apply这个方法,简单说一句,第一个参数是函数的this值,第二个数组是函数的参数。有兴趣看源码的童鞋应该对这个不陌生,我就不啰嗦了。
总之,这样一来,就把module里面的值注入给函数了。注意哦,调用函数是Injector这个对象调用的:
injector.invoke(add)//调用add函数
那么问题来了,既然给函数增加$inject属性可以注入依赖了,那么数组形式的调用怎么实现呢?其实这个也很简单。
回想一下,其实依赖注入的本质是通过一个key值,从cache对象里面拿到相应的数据,对吧?
在injector里面还有一个方法,叫做 annotate,中文意思是解剖,这个方法的作用就是把函数的依赖给解剖出来:比如这篇文章的例子,我们是通过$inject属性,把a,b两个key值拿出来的。看代码:
//数组形式依赖注入
['a','b',function add(a,b){}]; //直接调用的依赖注入
function (a,b){} //annonate方法的作用就是把上面两种形式里面的a,b这两个键值拿出来
其实对于数组形式依赖注入来说,键值很好拿,拆一下数组就行了。我就贴一下annotate的简单代码,很好懂的:
function annotate(fn){
//如果是一个数组 ['a','b',function(a,b){}]
//返回数组最后一个函数之前的所有部分
if(isArray(fn)){
return fn.slice(0,fn.length-1);
}else{
//如果不是数组,就返回这个函数的$inject属性
return fn.$inject;
}
}
就是这么简单!
好了,其实依赖注入更牛逼的是那个直接调用的依赖注入,这个篇幅比较长,我打算明天再更。
晚安了兄弟们。欢迎留言和我交流呀 :)
angular源码阅读3:真的,依赖注入的原理的更多相关文章
- angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。
最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇. 如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么? 这篇博客其实是angu ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- angular源码阅读的起点,setupModuleLoader方法
angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 ...
- .NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入
作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9998021.html 写在前面 上篇文章我给大家讲解了ASP.NET Core的概念及为什么使用它,接着 ...
- ABP源码分析六:依赖注入的实现
ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的实例定义注入 ...
- [Abp 源码分析]三、依赖注入
0.简要介绍 在 Abp 框架里面,无时无刻不存在依赖注入,关于依赖注入的作用与好处我就不在这里多加赘述了,网上有很多解释的教程.在 [Abp 源码分析]一.Abp 框架启动流程分析 里面已经说过,A ...
- 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程
一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- Spring源码解析-基于注解依赖注入
在spring2.5版本提供了注解的依赖注入功能,可以减少对xml配置. 主要使用的是 AnnotationConfigApplicationContext: 一个注解配置上下文 AutowiredA ...
随机推荐
- ssl双向认证和单向认证原理
有朋友在搞一个项目,周末有聊到一些安全性的东西,很自然会想起https,但https究竟如何实施,其原理又是什么? 基于ssl,一般的应用都是单向认证,如果应用场景要求对客户来源做验证也可以实现成双向 ...
- python 的var_dump
from __future__ import print_function from types import NoneType __author__ = "Shamim Hasnath&q ...
- JMeter学习-023-JMeter 命令行(非GUI)模式详解(一)-执行、输出结果及日志、简单分布执行脚本
前文 讲述了JMeter分布式运行脚本,以更好的达到预设的性能测试(并发)场景.同时,在前文的第一章节中也提到了 JMeter 命令行(非GUI)模式,那么此文就继续前文,针对 JMeter 的命令行 ...
- cordova IOS源码浅析
cordova封装了一套js和OC通信的代码,cordova.js下的iOSExex是关键的js去调原生的发起点. function iOSExec() { var successCallback, ...
- Java提高篇—— 简单介绍Java 的内存泄漏
java最明显的一个优势就是它的内存管理机制.你只需简单创建对象,java的垃圾回收机制负责分配和释放内存.然而情况并不像想像的那么简单,因为在Java应用中经常发生内存泄漏. 本教程演示了什么是内存 ...
- Linux CentOS 中安装 MySql
CentOS7的yum源中默认好像是没有mysql的.为了解决这个问题,我们要先下载mysql的repo源. 1. 下载mysql的repo源 $ wget http://repo.mysql.com ...
- Java jdbc 连接oracle之三(封装工具类)
driver = oracle.jdbc.driver.OracleDriver url = jdbc:oracle:thin:@192.168.10.105:1521:orcl user = LF ...
- SQL 一条SQL语句 统计 各班总人数,男女各总人数 ,各自 男女 比例 (转)
select sClass 班级,count(*) 班级学生总人数, sum(case when sGender=0 then 1 else 0 end) 女生人数, sum(case when ...
- java基础类型、包装器
char a = 'h'; //类包装器 Character aobj = a ;//自动装箱 byte b = 6; Byte bobj = b; short s = 234; Short sob ...
- 反编译android的apk
将要反编译的APK后缀名改为.rar或 .zip,并解压 得到其中的classes.dex文件(它就是java文件编译再通过dx工具打包而成的),将获取到的classes.dex放到之前解压出来的 ...