Angular 4.x Injector and angularinjector

Source: Internet
Author: User
Tags allkeys export class

Angular 4.x Injector and angularinjector

Before introducing Angular Injector (Injector), we must first understand Dependency Injection, that is, the concept of Dependency Injection.

Dependency injection allows the program design to follow the Dependency inversion principle (simply put, it requires the abstraction to be programmed, rather than the implementation to reduce the coupling between the client and the implementation module) the caller only needs to know the service interface, and the Injector is responsible for processing and providing the service Search and creation to the caller. In this way, the dependencies between the service and the caller are separated, compliant with low coupling programming principles.

According to the above content, dependency injection contains three roles: caller, service, and Injector ). Now we will introduce Injector. In Angular, Injector is used to manage the creation and acquisition of service objects. Next, let's take a look at the Injector abstract class:

Injector abstract class

// Angular2 \ packages \ core \ src \ di \ injector. tsexport abstract class Injector {static THROW_IF_NOT_FOUND = _ THROW_IF_NOT_FOUND; static NULL: Injector = new _ NullInjector ();/*** gets the corresponding object from the Injector Based on the given Token. * If no corresponding object is found, the value set by notFoundValue is returned. If the value of notFoundValue is equal to * _ THROW_IF_NOT_FOUND, an exception is thrown. */Abstract get <T> (token: Type <T> | InjectionToken <T>, notFoundValue? : T): T;} const _ THROW_IF_NOT_FOUND = new Object ();

The Injector abstract class definesget() Abstract method. This method is used to obtain the corresponding object from the Injector Based on the given Token. each subclass of the Injector abstract class must implement this method. In Angular, the common Injector abstract class subclasses include:

  1. _ NullInjector
  2. ReflectiveInjector

Next we will introduce them in sequence:

_ NullInjector class

An instance of the _ NullInjector class is used to indicate an empty injector.

// angular2\packages\core\src\di\injector.tsclass _NullInjector implements Injector { get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any { if (notFoundValue === _THROW_IF_NOT_FOUND) {  throw new Error(`No provider for ${stringify(token)}!`); } return notFoundValue; }}

ReflectiveInjector abstract class

ReflectiveInjector indicates a dependency injection container for instantiating objects and parsing dependencies.

ReflectiveInjector example

@Injectable()class Engine {}@Injectable()class Car { constructor(public engine:Engine) {}}var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);var car = injector.get(Car);expect(car instanceof Car).toBe(true);expect(car.engine instanceof Engine).toBe(true);

In the preceding example, we callresolveAndCreate() Method To create a injector. Then, callget() Method to obtain the object corresponding to the Token. This abstract class resolveAndCreate() Static methods include the following static methods:

  1. Resolve ()-resolution Provider list is ResolvedReflectiveProvider list
  2. FromResolvedProviders ()-creates a ReflectiveInjector object based on the ResolvedReflectiveProvider list

Next we will analyze the above static method:

ResolveAndCreate ()

static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector { const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);}

From the code above, we can see thatresolveAndCreate() The method is called internally. ReflectiveInjector.resolve() Method andReflectiveInjector.fromResolvedProviders() Method To create a ReflectiveInjector object.

Resolve ()

This method is used to resolve the Provider array to the ResolvedReflectiveProvider array.

static resolve(providers: Provider[]): ResolvedReflectiveProvider[] { return resolveReflectiveProviders(providers);}

Resolve () Example

@Injectable()class Engine {}@Injectable()class Car { constructor(public engine:Engine) {}}var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);expect(providers.length).toEqual(2);expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);expect(providers[0].key.displayName).toBe("Car");expect(providers[1].key.displayName).toBe("Engine");

Resolve () parsing illustration

 

Provider type

Export type Provider = TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any []; // ApiServiceexport interface TypeProvider extends Type <any >{}// {provide: ApiService, useClass: apiService} export interface ClassProvider {// used to set the Token value associated with the dependent object. The Token value may be an instance of Type, InjectionToken, OpaqueToken, or the string provide: any; useClass: type <any>; // identifies whether a multiple providers is returned. Type, the list of dependent objects associated with the Token is returned. multi? : Boolean;} // {provide: 'api _ url', useValue: 'http: // my.api.com/v1'} export interface ValueProvider {provide: any; useValue: any; multi? : Boolean;} // {provide: 'apiervicealias', useExisting: ApiService} export interface ExistingProvider {provide: any; useExisting: any; multi? : Boolean;} // {provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], multi: true} export interface FactoryProvider {provide: any; useFactory: Function; deps? : Any []; // set the dependency object of the factory function multi? : Boolean ;}

ResolvedReflectiveProvider Interface

Export interface ResolvedReflectiveProvider {// the unique object is used to obtain the object key: ReflectiveKey from the ReflectiveInjector; // The factory function is used to create the key-related dependent object resolvedFactories: resolvedreflective // identify whether the current provider is multi-provider multiProvider: boolean ;}

ResolvedReflectiveFactory class

export class ResolvedReflectiveFactory { constructor(  public factory: Function,  public dependencies: ReflectiveDependency[]) {}}

ReflectiveDependency class

export class ReflectiveDependency { constructor(  public key: ReflectiveKey,   public optional: boolean,   public visibility: Self|SkipSelf|null) {} static fromKey(key: ReflectiveKey): ReflectiveDependency { return new ReflectiveDependency(key, false, null); }}

ReflectiveKey class

The ReflectiveKey object contains two attributes: the unique id and token within the system range. Unique id within the system range, allowing the injector to store created objects in a more efficient way. In addition, we cannot manually create ReflectiveKey. When the ReflectiveInjector object resolves providers, the ReflectiveKey object is automatically created.

Export class ReflectiveKey {constructor (public token: Object, public id: number) {if (! Token) {throw new Error ('token must be defined! ') ;}} // Returns the serialized token get displayName (): string {return stringify (this. token);} // obtain the ReflectiveKey static get (token: Object) corresponding to the token: ReflectiveKey {return _ globalKeyRegistry. get (resolveForwardRef (token);} // obtain the number of registered reflectivekeys in the system static get numberOfKeys (): number {return _ globalKeyRegistry. numberOfKeys ;}} const _ globalKeyRegistry = new KeyRegistry (); // create a Key warehouse export class KeyRegistry {Private _ allKeys = new Map <Object, ReflectiveKey> ();/*** if the token is an instance of the ReflectiveKey class, it is returned directly. If the _ allKeys object contains the token attribute *, The ReflectiveKey object corresponding to the token is returned. Otherwise, create a new ReflectiveKey Object and * save it to the _ allKeys Object */get (token: Object): ReflectiveKey {if (token instanceof ReflectiveKey) return token; if (this. _ allKeys. has (token) {return this. _ allKeys. get (token )!;} Const newKey = new ReflectiveKey (token, ReflectiveKey. numberOfKeys); this. _ allKeys. set (token, newKey); return newKey;} // get the number of saved ReflectiveKey get numberOfKeys (): number {return this. _ allKeys. size ;}}

Analyzed resolve() The input parameters and return types of the method. Let's take a look at the specific implementation of this method:

Export function providers (providers: Provider []): ResolvedReflectiveProvider [] {const normalized = _ normalizeProviders (providers, []); // Step 1 const resolved = normalized. map (resolveReflectiveProvider); // Step 2 const resolvedProviderMap = mergeResolvedReflectiveProviders (resolved, new Map (); // step 3 return Array. from (resolvedProviderMap. values (); // Step 4}

Step 1: standardize the Provider

Const normalized = _ normalizeProviders (providers, []); // normalize Providersfunction _ normalizeProviders (providers: Provider [], res: Provider []): Provider [] {providers. forEach (B => {// providers: [Type] => providers: [{provide: Type, useClass: Type}] if (B instanceof Type) {res. push ({provide: B, useClass: B});} else if (B & typeof B = 'object' & (B as any ). provide! = Undefined) {res. push (B as NormalizedProvider);} else if (B instanceof Array) {// if B is an Array, call the _ normalizeProviders () method _ normalizeProviders (B, res) recursively );} else {throw invalidProviderError (B) ;}}); return res;} interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider {}

Step 2 -- convert NormalizedProvider to ResolvedReflectiveProvider

Const resolved = normalized. map (resolveReflectiveProvider); // parse NormalizedProvider as incluresolvereflectiveprovider (provider: NormalizedProvider): ResolvedReflectiveProvider {return new ResolvedReflectiveProvider _ (ReflectiveKey. get (provider. provide), [resolveReflectiveFactory (provider)], provider. multi | false) ;}// used to create the resolved Provider instance export class implements _ implements ResolvedReflectiveProvider {constructor (public key: ReflectiveKey, public resolvedFactories: Unknown [], public multiProvider: boolean) {} get resolvedFactory (): ResolvedReflectiveFactory {return this. resolvedFactories [0] ;}// parse the NormalizedProvider object and create the response object function compute (provider: NormalizedProvider): Using {let factoryFn: Function; let resolvedDeps: ReflectiveDependency []; if (provider. useClass) {// {provide: ApiService, useClass: ApiService} const useClass = resolveForwardRef (provider. useClass); factoryFn = reflector. factory (useClass); resolvedDeps = _ dependenciesFor (useClass);} else if (provider. useExisting) {// {provide: 'apiervicealias', useExisting: ApiService} factoryFn = (aliasInstance: any) => aliasInstance; resolvedDeps = [ReflectiveDependency. fromKey (ReflectiveKey. get (provider. useExisting)];} else if (provider. useFactory) {// {provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], // multi: true} factoryFn = provider. useFactory; resolvedDeps = constructDependencies (provider. useFactory, provider. deps);} else {// {provide: 'api _ url', useValue: 'http: // my.api.com/v1'} factoryFn = () => provider. useValue; // const _ EMPTY_LIST: any [] = []; resolvedDeps = _ EMPTY_LIST;} return new ResolvedReflectiveFactory (factoryFn, resolvedDeps );}

Step 3 -- merge parsed providers

Const resolvedProviderMap = histogram (resolved, new Map (); export function compute (providers: outputs [], normalizedProvidersMap: Map <number, ResolvedReflectiveProvider>): Map <number, resolvedReflectiveProvider> {for (let I = 0; I <providers. length; I ++) {const provider = providers [I]; // obtain the key from the normalizedProvidersMap object. reso corresponding to the id LvedReflectiveProvider object const existing = normalizedProvidersMap. get (provider. key. id); if (existing) {// if the current provider is not multi provider, an exception is thrown if (provider. multiProvider! = Existing. multiProvider) {throw mixingMultiProvidersWithRegularProvidersError (existing, provider);} // if the current provider is multi provider, add each item in the resolvedFactories // list of the current provider to the resolvedFactories list of the existing provider object. If (provider. multiProvider) {for (let j = 0; j <provider. resolvedFactories. length; j ++) {existing. resolvedFactories. push (provider. resolvedFactories [j]) ;}} else {// if the current provider is not a multi provider, it overwrites the existing provider normalizedProvidersMap. set (provider. key. id, provider) ;}} else {let resolvedProvider: ResolvedReflectiveProvider; // if the current provider is multi provider, a new ResolvedReflectiveProvider object if (provider. multiProvider) {resolvedProvider = new ResolvedReflectiveProvider _ (provider. key, provider. resolvedFactories. slice (), provider. multiProvider);} else {resolvedProvider = provider;} // Save the resolved ResolvedReflectiveProvider object normalizedProvidersMap in normalizedProvidersMap. set (provider. key. id, resolvedProvider) ;}return normalizedProvidersMap ;}

Step 4: Generate ResolvedReflectiveProvider []

// ResolvedProviderMap values, create ResolvedReflectiveProvider [] Array. from (resolvedProviderMap. values ();/*** create a new array instance based on a similar array or iteratable object ** arrayLike: convert it to a class array object of a real array or a traversal object. * MapFn (optional): If this parameter is specified, the final generated array is processed by this function and then returned. * ThisArg (optional): the value of this when mapFn function is executed. */Array. from (arrayLike [, mapFn [, thisArg])

FromResolvedProviders ()

This method is used to create a injector Based on the parsed providers.

static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent?: Injector): ReflectiveInjector {  return new ReflectiveInjector_(providers, parent);}

FromResolvedProviders () Example

@Injectable()class Engine {}@Injectable()class Car { constructor(public engine:Engine) {}}var providers = ReflectiveInjector.resolve([Car, Engine]);var injector = ReflectiveInjector.fromResolvedProviders(providers);expect(injector.get(Car) instanceof Car).toBe(true);

Complete fromResolvedProviders() How to use the method. Next we will focus on the analysisReflectiveInjector_ Class.

ReflectiveInjector _ class

ReflectiveInjector _ class attributes

// ConstructionCounter: number = 0; // ResolvedReflectiveProvider list public _ providers: ResolvedReflectiveProvider []; // parent-level Injector public _ parent: Injector | null; // ReflectiveKey id list keyIds: number []; // dependent Object List objs: any [];

ReflectiveInjector _ Constructor

Export class ReflectiveInjector _ implements ReflectiveInjector {constructor (_ providers: ResolvedReflectiveProvider [], _ parent? : Injector) {this. _ providers = _ providers; // set the parent class injector this. _ parent = _ parent | null; const len = _ providers. length; this. keyIds = new Array (len); this. objs = new Array (len); // initialize the keyIds list and objs Object List for (let I = 0; I <len; I ++) {this. keyIds [I] = _ providers [I]. key. id; this. objs [I] = UNDEFINED ;}} const UNDEFINED = new Object ();

ReflectiveInjector _ Class Method

There are many methods in the ReflectiveInjector _ class. We only analyze the most important methods. First, we first classify them based on the functions implemented by the methods:

  1. Used to create a ReflectiveInjector Injector
  2. Used to obtain objects
  3. Used to create objects
  4. Used to obtain factory function dependency objects

Used to create a ReflectiveInjector Injector

// Create a subinjector resolveAndCreateChild (providers: Provider []) based on the Provider list: ReflectiveInjector {const ResolvedReflectiveProviders = ReflectiveInjector. resolve (providers); return this. parser (response);} // Based on the resolved ResolvedReflectiveProvider list, create a subinjector handler (providers: Handler []): ReflectiveInjector {const inj = new ReflectiveInjector _ (providers ); inj. _ parent = this; return inj ;}

Used to obtain objects

// Get the parent Injector get parent (): Injector | null {return this. _ parent;} // get the token's dependent object get (token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {return this. _ getByKey (ReflectiveKey. get (token), null, notFoundValue);} // obtain the corresponding dependent object private _ getByKey (key: ReflectiveKey, visibility: Self | SkipSelf | null, notFoundValue: any): any {// const INJECTOR_KEY = ReflectiveKey. get (I Njector); if (key = INJECTOR_KEY) {return this;} // determines whether the dependency object is defined using the @ Self modifier, indicates obtaining the dependent object if (visibility instanceof Self) {return this. _ getByKeySelf (key, notFoundValue);} else {// use the default method to obtain the dependent object return this. _ getByKeyDefault (key, notFoundValue, visibility) ;}}// obtain the dependent object _ getByKeySelf (key: ReflectiveKey, notFoundValue: any) from the base Injector: any {const obj = this. _ getObjByKeyId (key. id); return (obj! = UNDEFINED )? Obj: this. _ throwOrNull (key, notFoundValue);} // use the default method to obtain the dependent object _ getByKeyDefault (key: ReflectiveKey, notFoundValue: any, visibility: Self | SkipSelf | null ): any {let inj: Injector | null; // determines whether the dependent object is defined using the @ SkipSelf modifier, indicating that the dependent object if (visibility instanceof SkipSelf) is not obtained from the base-level Injector) {inj = this. _ parent;} else {inj = this;} // obtain the dependent object from the base-level injector. If the base-level injector cannot be obtained, search for while (inj instanceof ReflectiveInjector _) from the parent injector _) {const inj _ = <ReflectiveInjector _> inj; const obj = inj _. _ getObjByKeyId (key. id); if (obj! = UNDEFINED) return obj; inj = inj _. _ parent;} if (inj! = Null) {return inj. get (key. token, notFoundValue);} else {return this. _ throwOrNull (key, notFoundValue) ;}// obtain the object corresponding to the keyId. If the dependent object is not created, call the _ new () method to create one, then save it to // this. in the objs Object List, private _ getObjByKeyId (keyId: number): any {for (let I = 0; I <this. keyIds. length; I ++) {if (this. keyIds [I] === keyId) {// const UNDEFINED = new Object (); if (this. objs [I] === UNDEFINED) {this. objs [I] = this. _ new (this. _ providers [I]);} return this. objs [I] ;}} return UNDEFINED ;}

Used to create objects

// Create dependency object _ new (provider: ResolvedReflectiveProvider): any {// determine whether a circular dependency if (this. _ constructionCounter ++> this. _ getMaxNumberOfObjects () {throw metrics icdependencyerror (this, provider. key);} return this. _ instantiateProvider (provider);} // obtain the maximum number of objects. private _ getMaxNumberOfObjects (): number {return this. objs. length;} // create a dependency object based on the resolved provider. If it is multi provider, create a multi provider object cyclically. Private _ instantiateProvider (provider: ResolvedReflectiveProvider): any {if (provider. multiProvider) {const res = new Array (provider. resolvedFactories. length); for (let I = 0; I <provider. resolvedFactories. length; ++ I) {res [I] = this. _ instantiate (provider, provider. resolvedFactories [I]);} return res;} else {return this. _ instantiate (provider, provider. resolvedFactories [0]) ;}// create a dependency object private _ instantiate (provider: ResolvedReflectiveProvider, ResolvedReflectiveFactory: ResolvedReflectiveFactory) based on the resolved provider and the resolved factory ): any {// obtain the object factory function const factory = ResolvedReflectiveFactory. factory; // obtain the list of objects on which the factory function depends. let deps: any []; try {deps = ResolvedReflectiveFactory. dependencies. map (dep => this. _ getByReflectiveDependency (dep);} catch (e) {if (e. addKey) {e. addKey (this, provider. key);} throw e;} // call the object factory function to create the dependent object let obj: any; try {obj = factory (... deps);} catch (e) {throw instantiationError (this, e, e. stack, provider. key);} return obj ;}

Used to obtain factory function dependency objects

// If the dependency object is defined using the @ Optional modifier, it indicates that the dependency object is Optional. If no value is obtained, null is returned. Private _ getByReflectiveDependency (dep: ReflectiveDependency): any {return this. _ getByKey (dep. key, dep. visibility, dep. optional? Null: THROW_IF_NOT_FOUND );}

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.