Brief introduction
Injector is used for automatic injection of parameters, such as
function fn ($http, $scope, Aservice) {
}
Ng runs the $http, $scope, and Aservice is automatically passed in as a parameter for execution.
It's easy to understand that Injector did two things.
- Cache those service and inject it later as a parameter
- Analyze the parameter list to find the required parameter injection
The following source analysis how to achieve the above two things.
Structure
createInjector -> createInternalInjector return: instanceInjector
So Createinjector () returns the Instanceinjector, which is structured as follows:
{
Invoke:invoke,
instantiate:instantiate,
get:getservice,
annotate:annotate,
has: function (name) {return
providercache.hasownproperty (name + providersuffix) | | cache.hasownproperty (name);
}
}
SOURCE Analysis
1. Createinjector
function Createinjector (modulestoload, strictdi) {strictdi = (Strictdi = = true); var instantiating = {}, Providersuffix = ' Provider ', Path = [], loadedmodules = new HashMap ([], true),//preconfigured $provide, for Loadmodules call Registration Service Providercache = {$provide: {provider:supportobject (provider), FAC Tory:supportobject (Factory), Service:supportobject (service), Value:supportobject (value), CONSTANT:SU Pportobject (constant), decorator:decorator},//Providerinjector, instanceinjector two injector//Insta Nceinjector to provide service and other injection, providerinjector internal supply provider to obtain Providerinjector = (providercache. $injector = Createinter Nalinjector (Providercache, function () {Throw $injectorMinErr (' UNPR ', ' Unknown provider: {0} ', Path.join (' <-'))
; }, Strictdi)), Instancecache = {}, Instanceinjector = (instancecache. $injector = Createinternalinjector (instanc Ecache, function (servicename) {var provider =Providerinjector.get (servicename + providersuffix);
Return Instanceinjector.invoke (provider. $get, provider, undefined, servicename);
}, STRICTDI));
Load Module 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
For packing method, the method before wrapping accepts two parameters (key, value), after wrapping method can support incoming object parameter, namely multiple key-> value.
function SupportObject (delegate) {return
function (key, value) {
if (IsObject (key)) {
ForEach key, Reverseparams (delegate));
else {return
delegate (key, value);}}
;
}
2.2 Provider
Review the way provider, service and factory are used
App.factory (' ServiceName ', function () {return {getname:function () {}, Setname:function () {}}});
App.service (' ServiceName ', function () {this.getname = function () {} this.setname = function () {}}); App.provider (' ServiceName ', function ($httpProvider) {//inject $httpprovider this. $get = function () {return {getName:
function () {}, Setname:function () {}};
}
});
App.provider (' ServiceName ', {$get: function () {}});
function provider (name, Provider_) {assertnothasownproperty (name, ' service '); When Provider_ is FN or array, other provider can be injected into the parameter//because Providerinjector.instantiate (provider_) can be passed on to other dependent provider// This is also provider and Service,factory method is not the same place if (Isfunction (provider_) | | IsArray (PROVIDER_)) {Provider_ = providerinjector.in
Stantiate (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. Inst
Antiate (constructor);
}]);
function value (name, Val) {return factory (name, Valuefn (Val));}
The final rollup to the provider implementation is to cache provider to Providercache for calling
Unlike other things, the implementation of constant is saved to Providercache and Instancecache, which can be injected into the definition of provider or the definition of service.
function constant (name, value) {
assertnothasownproperty (name, ' constant ');
Providercache[name] = value;
Instancecache[name] = value;
}
2.3 Review Loadmodules
function Runinvokequeue (queue) {
var i, II;
for (i = 0, ii = queue.length i < II i++) {
var Invokeargs = queue[i],
Provider = Providerinjector.get (Invok Eargs[0]);
In the format of the queue ($provide, factory, arguments]
//Replaced, $provide. factory.apply ($provide, arguments);
is to call the $provid Factory,service et
provider[invokeargs[1]].apply (provider, invokeargs[2]);
}
2.4 Decorator
Example:
Module.config (function ($provide) {
$provide. Decorator (' Mail ', function ($delegate) {
$delegate. ADDCC = function (cc) {
this.cc.push (cc);
};
return $delegate;})
Using the example, the passed parameter $delegate is the original service instance, and you need to add methods on that instance, that is, the so-called adorner
Source:
function Decorator (ServiceName, DECORFN) {
var origprovider = providerinjector.get (ServiceName + providersuffix),
orig$get = Origprovider. $get;
Origprovider. $get = function () {
//The required service instance is generated through the provider obtained above, and then injected into the parameter list $delegate with the
var originstance = Instanceinjector.invoke (Orig$get, origprovider);
Return Instanceinjector.invoke (DECORFN, NULL, {$delegate: Originstance});}
3. Createinternalinjector
3.1 Overall structure
Get from cache, no words call factory to create, specifically see GetService parsing
function Createinternalinjector (cache, factory) {
function GetService (serviceName) {
}
function Invoke ( FN, self, locals, serviceName) {
}
function Instantiate (Type, locals, serviceName) {
} return
{
// EXECUTE FN, with parameter injection function
Invoke:invoke,
//instantiate FN, parameters can be injected into
instantiate:instantiate,
//Get provider or service
Get:getservice,
//Get the parameter list of the method for injection using
annotate:annotate,
//Confirm that provider or service
has is included: function (name) {return
providercache.hasownproperty (name + providersuffix) | | cache.hasownproperty (name);
}
};
}
3.2 Annotate
Get the parameter list for FN
Type1
function fn (A, B, c)-> [' A ', ' B ', ' C ']
//type2
[' A ', ' B ', fn]-> [' A ', ' B ']
//Type3
function fn () {}
fn. $inject = [' A ', ' C ']
-> [' A ', ' C ']
Source:
Function Annotate (FN, STRICTDI, name) {var $inject, Fntext, Argdecl, last; if (typeof fn = = ' function ') {if (!) (
$inject = fn. $inject)) {$inject = []; if (fn.length) {//in strict mode or if (STRICTDI) {if (!isstring (name) | |!name) {name = Fn.name | | ANONFN (f
n); } Throw $injectorMinErr (' strictdi ', ' {0} is not using explicit annotation and cannot being invoked in strict mode '
, name);
///Remove the comment Fntext = fn.tostring (). Replace (strip_comments, ");
All parameters are selected FN (a,b,c,d)-> ' a,b,c,d ' argdecl = Fntext.match (Fn_args); Split into Array ForEach (Argdecl[1].split (Fn_arg_split), function (ARG) {arg.replace (Fn_arg, function (all, underscore,
Name) {$inject. push (name);
});
});
fn. $inject = $inject;
} else if (IsArray (FN)) {last = fn.length-1;
ASSERTARGFN (Fn[last], ' FN ');
$inject = Fn.slice (0, last);
else {ASSERTARGFN (FN, ' fn ', true);
return $inject;
}
3.3 GetService
3.4 Invoke
function Invoke (FN, self, locals, serviceName) {
if (typeof locals = = ' string ') {
serviceName = locals;
locals = null;
}
var args = [],
//Get parameter list
$inject = Annotate (FN, STRICTDI, serviceName),
length, I,
key;
for (i = 0, length = $inject. length; i < length; i++) {
key = $inject [i];
if (typeof key!== ' string ') {
throw $injectorMinErr (' itkn ',
' incorrect injection ' token! Expected service name As String, got {0} ', key ';
}
Locals priority
Args.push (
locals && locals.hasownproperty (key)
Locals[key]
: GetService ( Key)
);
if (IsArray (FN)) {
fn = fn[length];
}
Return fn.apply (self, args);
3.5 Instantiate
function instantiate (Type, locals, serviceName) {
var constructor = function () {},
instance, returnedvalue;
When Type is array, gets the last argument, such as: [' $window ', function ($win) {}]
Constructor.prototype = (IsArray (Type)? TYPE[TYPE.LENGTH-1]: Type). prototype;
Instance = new constructor ();
Invoke invoke Execute type method
Returnedvalue = Invoke (type, instance, locals, serviceName);
Return IsObject (returnedvalue) | | Isfunction (returnedvalue)? returnedvalue:instance;
}
The role of instantiate is to instantiate the type, which can be automatically passed in to the constructor during instantiation.