angularjs 源码解析之injector

injector是用来做参数自动入的,例如

function fn ($http, $scope, aService) { }

ng在运行时会把$http, $scope, aService 自动作为参数传入进行执行。

其实很容易想明白,injector做了两件事

缓存那些service,以后作为参数

分析参数列表,找到需要的参数注入

下面源码分析如何实现上面两件事情。

结构

createInjector -> createInternalInjector  return: instanceInjector

所以 createInjector() 返回的是 instanceInjector,结构如下:

{ invoke: invoke, instantiate: instantiate, get: getService, annotate: annotate, has: function(name) { return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); } }

源码分析

1. createInjector

function createInjector(modulesToLoad, strictDi) { strictDi = (strictDi === true); var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], loadedModules = new HashMap([], true), // 预先配置$provide,供loadModules中调用注册service等 providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, // providerInjector, instanceInjector 两个注入器 // instanceInjector对外提供service等注入,providerInjector对内提供provider获取 providerInjector = (providerCache.$injector = createInternalInjector(providerCache, function() { throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); }, strictDi)), instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) { var provider = providerInjector.get(servicename + providerSuffix); return instanceInjector.invoke(provider.$get, provider, undefined, servicename); }, strictDi)); // 加载模块 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); return instanceInjector; }

2. $provide

$provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator }

2.1 supportObject

用于包装方法,包装前的方法接受两个参数 (key, value),经过包装后的方法能支持传入object参数,即多个 key -> value。

function supportObject(delegate) { return function(key, value) { if (isObject(key)) { forEach(key, reverseParams(delegate)); } else { return delegate(key, value); } }; }

2.2 provider

回顾下provider、service 和 factory的使用方式

app.factory('serviceName', function(){ return { getName: function(){}, setName: function(){} } }); app.service('serviceName', function(){ this.getName = function() {} this.setName = function() {} }); app.provider('serviceName', function($httpProvider){ // 注入$httpProvider this.$get = function() { return { getName: function(){}, setName: function(){} }; } }); app.provider('serviceName', { $get: function () {} }); function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); // 当provider_是fn或者array时可以将其他provider注入到参数 // 因为providerInjector.instantiate(provider_)时可以传入依赖的其他provider // 这也是provider与service,factory方法不一样的地方 if (isFunction(provider_) || isArray(provider_)) { provider_ = providerInjector.instantiate(provider_); } if (!provider_.$get) { throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); } return providerCache[name + providerSuffix] = provider_; } function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } function value(name, val) { return factory(name, valueFn(val)); }

最终汇总到provider的实现,将provider缓存到providerCache,供调用

跟其他不一样的就是constant的实现,分别保存到providerCache和instanceCache中,这样在定义provider还是在定义service是都能注入。

function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; }

2.3 回顾 loadModules

function runInvokeQueue(queue) { var i, ii; for(i = 0, ii = queue.length; i < ii; i++) { var invokeArgs = queue[i], provider = providerInjector.get(invokeArgs[0]); // 存入queue的如格式[$provide, factory, arguments] // 经过替换,$provide.factory.apply($provide, arguments); // 就是调用$provid的factory,service等 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } }

2.4 decorator

示例:

module.config(function($provide) { $provide.decorator('Mail', function($delegate) { $delegate.addCC = function(cc) { this.cc.push(cc); }; return $delegate; }); })

使用示例看出,传入的参数$delegate是原先的service实例,需要在该实例上添加方法都可以,即所谓的装饰器

源码:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzfsdz.html