angularjs 源码解析之injector
简介
injector是用来做参数自动注入的,例如
functionfn($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){ returnproviderCache.hasOwnProperty(name+providerSuffix)||cache.hasOwnProperty(name); } }
源码分析
1.createInjector
functioncreateInjector(modulesToLoad,strictDi){ strictDi=(strictDi===true); varINSTANTIATING={}, providerSuffix='Provider', path=[], loadedModules=newHashMap([],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',"Unknownprovider:{0}",path.join('<-')); },strictDi)), instanceCache={}, instanceInjector=(instanceCache.$injector= createInternalInjector(instanceCache,function(servicename){ varprovider=providerInjector.get(servicename+providerSuffix); returninstanceInjector.invoke(provider.$get,provider,undefined,servicename); },strictDi)); //加载模块 forEach(loadModules(modulesToLoad),function(fn){instanceInjector.invoke(fn||noop);}); returninstanceInjector; }
2.$provide
$provide:{ provider:supportObject(provider), factory:supportObject(factory), service:supportObject(service), value:supportObject(value), constant:supportObject(constant), decorator:decorator }
2.1supportObject
用于包装方法,包装前的方法接受两个参数(key,value),经过包装后的方法能支持传入object参数,即多个key->value。
functionsupportObject(delegate){ returnfunction(key,value){ if(isObject(key)){ forEach(key,reverseParams(delegate)); }else{ returndelegate(key,value); } }; }
2.2provider
回顾下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(){} }); functionprovider(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}'mustdefine$getfactorymethod.",name); } returnproviderCache[name+providerSuffix]=provider_; } functionfactory(name,factoryFn){returnprovider(name,{$get:factoryFn});} functionservice(name,constructor){ returnfactory(name,['$injector',function($injector){ return$injector.instantiate(constructor); }]); } functionvalue(name,val){returnfactory(name,valueFn(val));}
最终汇总到provider的实现,将provider缓存到providerCache,供调用
跟其他不一样的就是constant的实现,分别保存到providerCache和instanceCache中,这样在定义provider还是在定义service是都能注入。
functionconstant(name,value){ assertNotHasOwnProperty(name,'constant'); providerCache[name]=value; instanceCache[name]=value; }
2.3回顾loadModules
functionrunInvokeQueue(queue){ vari,ii; for(i=0,ii=queue.length;i<ii;i++){ varinvokeArgs=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.4decorator
示例:
module.config(function($provide){ $provide.decorator('Mail',function($delegate){ $delegate.addCC=function(cc){ this.cc.push(cc); }; return$delegate; }); })
使用示例看出,传入的参数$delegate是原先的service实例,需要在该实例上添加方法都可以,即所谓的装饰器
源码:
functiondecorator(serviceName,decorFn){ varorigProvider=providerInjector.get(serviceName+providerSuffix), orig$get=origProvider.$get; origProvider.$get=function(){ //通过上面获取的provider生成需要的service实例,再以$delegate注入到参数列表 varorigInstance=instanceInjector.invoke(orig$get,origProvider); returninstanceInjector.invoke(decorFn,null,{$delegate:origInstance}); }; }
3.createInternalInjector
3.1整体结构
//从cache中获取,没有的话调用factory进行创建,具体看getService解析
functioncreateInternalInjector(cache,factory){ functiongetService(serviceName){ } functioninvoke(fn,self,locals,serviceName){ } functioninstantiate(Type,locals,serviceName){ } return{ //执行fn,具有参数注入功能 invoke:invoke, //实例化fn,可以参数注入 instantiate:instantiate, //获取provider或者service get:getService, //获取方法的参数列表,供注入使用 annotate:annotate, //确认是否含有provider或service has:function(name){ returnproviderCache.hasOwnProperty(name+providerSuffix)||cache.hasOwnProperty(name); } }; }
3.2annotate
获取fn的参数列表
//type1 functionfn(a,b,c)->['a','b','c'] //type2 ['a','b',fn]->['a','b'] //type3 functionfn(){} fn.$inject=['a','c'] ->['a','c']
源码:
functionannotate(fn,strictDi,name){ var$inject, fnText, argDecl, last; if(typeoffn==='function'){ if(!($inject=fn.$inject)){ $inject=[]; if(fn.length){ //严格模式下或抛错 if(strictDi){ if(!isString(name)||!name){ name=fn.name||anonFn(fn); } throw$injectorMinErr('strictdi', '{0}isnotusingexplicitannotationandcannotbeinvokedinstrictmode',name); } //将注释去掉 fnText=fn.toString().replace(STRIP_COMMENTS,''); //将参数全部选出fn(a,b,c,d)->'a,b,c,d' argDecl=fnText.match(FN_ARGS); //分割成array forEach(argDecl[1].split(FN_ARG_SPLIT),function(arg){ arg.replace(FN_ARG,function(all,underscore,name){ $inject.push(name); }); }); } fn.$inject=$inject; } }elseif(isArray(fn)){ last=fn.length-1; assertArgFn(fn[last],'fn'); $inject=fn.slice(0,last); }else{ assertArgFn(fn,'fn',true); } return$inject; }
3.3getService
//当cache中没有该service时,进入else,先cache[serviceName]=INSTANTIATING做一个标记 //因为接下来调用factory(serviceName),其实是一个递归调用 //function(servicename){ //varprovider=providerInjector.get(servicename+providerSuffix); //returninstanceInjector.invoke(provider.$get,provider,undefined,servicename); //} //instanceInjector.invoke(provider.$get时会将需要注入的参数get出来然后注入 //因此做上标记后就可以判断是否有循环依赖 functiongetService(serviceName){ if(cache.hasOwnProperty(serviceName)){ if(cache[serviceName]===INSTANTIATING){ throw$injectorMinErr('cdep','Circulardependencyfound:{0}', serviceName+'<-'+path.join('<-')); } returncache[serviceName]; }else{ try{ path.unshift(serviceName); cache[serviceName]=INSTANTIATING; returncache[serviceName]=factory(serviceName); }catch(err){ if(cache[serviceName]===INSTANTIATING){ deletecache[serviceName]; } throwerr; }finally{ path.shift(); } } }
3.4invoke
functioninvoke(fn,self,locals,serviceName){ if(typeoflocals==='string'){ serviceName=locals; locals=null; } varargs=[], //获取参数列表 $inject=annotate(fn,strictDi,serviceName), length,i, key; for(i=0,length=$inject.length;i<length;i++){ key=$inject[i]; if(typeofkey!=='string'){ throw$injectorMinErr('itkn', 'Incorrectinjectiontoken!Expectedservicenameasstring,got{0}',key); } //locals优先 args.push( locals&&locals.hasOwnProperty(key) ?locals[key] :getService(key) ); } if(isArray(fn)){ fn=fn[length]; } returnfn.apply(self,args); }
3.5instantiate
functioninstantiate(Type,locals,serviceName){ varConstructor=function(){}, instance,returnedValue; //当type为array时,获取最后的参数如:['$window',function($win){}] Constructor.prototype=(isArray(Type)?Type[Type.length-1]:Type).prototype; instance=newConstructor(); //调用invoke执行Type方法 returnedValue=invoke(Type,instance,locals,serviceName); returnisObject(returnedValue)||isFunction(returnedValue)?returnedValue:instance; }
instantiate的作用是用来实例化Type的,在实例化的过程中可以自动传入参数到构造函数。