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实例,需要在该实例上添加方法都可以,即所谓的装饰器
源码: