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的,在实例化的过程中可以自动传入参数到构造函数。