Angular Dependency Injection Introduction

       Dependency Injection (DI - Dependency Injection) is an important application design patterns. Angular which also has its own DI framework when designing applications often use it, it can develop our efficiency and modularity.

       Dependence, or the object when the service class is needed to perform its functions, required. DI is a coding mode in which the class will get a request from an external source dependency, without the need to create them ourselves.

       Angular system by adding @Injectable decorator on the class to tell the class system (service) is injected. Of course this is just tell Angular systems of this class (service) category can be injected. But the service can use where, by whom, to rely on injectors and providers to determine together.

  • Injector: The injector is responsible for the service instance is created, and injected them into the class you want to inject in. To determine the scope and service lifecycle services.

  • Providers: Who provides the service. Angular itself can not automatically determine if you are going to create an instance of the class of service on their own, or wait for the injection to create it. If you want to create through the injector, you must specify the service provider for each service in each injector inside.

An injection device (Injector)

       Angular dependence of the injector injection (Injector), used to manage services. Including the creation of services, access to services.

       Angular dependence of the injector in the injection system (Injector) is a multi-stage. In fact, the application has a component parallel to the tree injection tree. You can reconfigure the injector assembly on any level in the tree, into the provider.

       Another point to pay special attention, Angular injector is bubbling mechanism. When the application is a component when a dependency, Angular first try of the injector assembly to meet their it. If the injector assembly corresponding to the provider is not found, it passes the request is forwarded to its parent component injection to handle. If the current injector can not meet this request, it will continue to be transferred to its parent injector in the injector tree. The application continues to bubble up - until Angular find a can handle this application far beyond the injector or the component tree ancestors position. If you exceed the component tree ancestors have not found, Angular will throw an error.

Everything ultimately all service components. In fact, each injector assembly comprises two parts: the injector assembly itself, where the assembly NgMoudle corresponding injector.

       In our system, we can think Angular NgModule an injector, Component is an injector.

1.1 NgMoudle (module) stage injector

       NgMoudle (module) will tell Angualr-injection system to effect service on NgModule. This server can be used on all components NgModule this range. NgModule stage injection services in two ways: one is metadata providers @NgModule () specified, another option is to directly providedIn @Injectable () specified in the module class.

       NgMoudle stage injector in two ways: @NgModule () providers specified in the metadata, or a designated class module directly @Injectable () of providedIn option.

1.1.1 By @NgModule () is injected into the service providers in NgModule

       By @NgModule () is injected into the NgModule service providers, the service can only be used to limit the current NgModule inside.

export interface NgModule {
  ...
  
  /**
   * 本模块需要创建的服务。这些服务可以在本模块的任何地方使用。
   * NgModule我们可以认为是注入器,Provider是提供商。注入器通过提供商的信息来创建服务
   */
  providers?: Provider[];
  
  ...
}

About Provider (provider) more specific user we will do a detailed explanation below.

       For example, the following code we first define a NgmoduleProvidersService service class. The current class just add @Injectable () tells Angular system which is an injection services.

import {Injectable} from '@angular/core';

/**
 * 我们将在NgmoduleProvidersModule中注入该服务。
 * 然后在NgmoduleProvidersComponent里面使用该服务
 */
@Injectable()
export class NgmoduleProvidersService {

    constructor() {
    }

    // TODO:其他逻辑
}

       Next, think of all the places in NgmoduleProvidersModule use the service module inside. Quite simply, we specify this class NgmoduleProvidersService in @NgModule metadata providers inside enough. (TOKEN service is NgmoduleProvidersService, but also his own provider. Provider about more specific usage, we will explain in detail below)

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgmoduleProvidersComponent} from './ngmodule-providers.component';
import {NgmoduleProvidersRoutingModule} from './ngmodule-providers-routing.module';
import {NgmoduleProvidersService} from './ngmodule-providers.service';

@NgModule({
    declarations: [
        NgmoduleProvidersComponent
    ],
    providers: [
        NgmoduleProvidersService,
    ],
    imports: [
        CommonModule,
        NgmoduleProvidersRoutingModule
    ]
})
export class NgmoduleProvidersModule {
}

1.1.2 By @Injectable () is injected into providedIn service in NgModule

       @Injectable () decorator which metadata can specify NgModue providedIn directly. To inform the service can be used where. ProvidedIn may have three values: one is the Type is NgModule, one is the string 'root', one is null.

export interface InjectableDecorator {
    /**
     * providedIn有三种值:Type<any>、 ‘root’、 null
     * Type<any>指的是NgModule
     */
    (): TypeDecorator;
    (options?: {
        providedIn: Type<any> | 'root' | null;
    } & InjectableProvider): TypeDecorator;
    new (): Injectable;
    new (options?: {
        providedIn: Type<any> | 'root' | null;
    } & InjectableProvider): Injectable;
}

When providedIn is null time. Let's just tell the system class should be injected. In other places they can not be used. If you want to specify the Component NgModule decorator or decorator in which metadata providers.

1.1.2.1 providedIn: ‘root’

       providedIn: 'root'. We can simply think root string that represents the top AppModule. Indicates that the current service which can be applied throughout Angular use. But in the entire Angular application is only one service instance.

       For example, the following code, we define a StartupService service class. providedIn: 'root'. The
StartupService place like this task is injected using the current project. And it is all the same instance of an object.

import {Injectable} from '@angular/core';

/**
 * StartupService可以在系统的任务地方使用
 */
@Injectable({
    providedIn: 'root'
})
export class StartupService {

    constructor() {
    }

    // TODO: 其他逻辑
}

1.1.2.2 providedIn: NgModule

       providedIn: NgModule. Specify a NgModule directly through providedIn. Let the current service only in the specified NgModule inside.

       And providedIn: NgModule this case can shake the tree optimization. As long as @Injectable service itself () decorator specified provider, rather than rely on the service in the metadata of NgModule or components specified, you can create a shake tree optimization provider. The current assumption that this NgModule is lazy loaded.

Refers to a tree shaking compiler optimization option, meaning that the application does not reference the code is removed from the over-the finally produced package. If the provider can shake the tree is optimized, Angular compiler will remove the application code never used services from the final output content. This can significantly reduce your packaging volume.

       providedIn: When NgModule use of a special place to pay special attention. For example we want to use such services in NgmoduleProvidersModule NgmoduleProviderInModuleService module. The following wording is wrong.

import { Injectable } from '@angular/core';
import {NgmoduleProvidersModule} from './ngmodule-providers.module';

@Injectable({
  providedIn: NgmoduleProvidersModule
})
export class NgmoduleProviderInModuleService {

  constructor() { }
}

Compile time will throw a warning message, but the compiler.

WARNING in Circular dependency detected:

       To solve this anomaly information, so that the code can be compiled normally, we need the help of a NgModule (NgmoduleProvidersResolveModule name you just come) the transition to the next. The transition NgModule assigned to providedIn. Finally, we really want to use the service NgModule which imports this transition NgModule. He said a little going around in. We directly use the code to illustrate.

// 需要在模块NgmoduleProvidersModule里面使用的服务NgmoduleProviderInModuleService

import {Injectable} from '@angular/core';
import {NgmoduleProvidersResolveModule} from './ngmodule-providers-resolve.module';

/**
 * providedIn中直接指定了当前服务可以在哪个模块使用
 * 特别说明:我们想在NgmoduleProvidersModule模块里面使用该服务,
 * 如果providedIn直接写NgmoduleProvidersModule,会报编译错误,
 * 所以我们定义了一个中间模块NgmoduleProvidersResolveModule,
 * 然后在NgmoduleProvidersModule里面引入了NgmoduleProvidersResolveModule。
 *
 * NgmoduleProvidersResolveModule相当于一个过渡的作用
 */
@Injectable({
    providedIn: NgmoduleProvidersResolveModule
})
export class NgmoduleProviderInModuleService {

    constructor() {
    }

    // TODO: 其他逻辑
}


// 过渡NgModule NgmoduleProvidersResolveModule

import {NgModule} from '@angular/core';

/**
 * providedIn: NgModule的时候NgModule不能直接写对应的NgModule,
 * 需要一个过渡的NgModule。否则编译报错:WARNING in Circular dependency detected
 */
@NgModule({
})
export class NgmoduleProvidersResolveModule {
}



// NgmoduleProvidersModule 服务将在该模块里面使用。

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgmoduleProvidersComponent} from './ngmodule-providers.component';
import {NgmoduleProvidersRoutingModule} from './ngmodule-providers-routing.module';
import {NgmoduleProvidersService} from './ngmodule-providers.service';
import {NgmoduleProvidersResolveModule} from './ngmodule-providers-resolve.module';


@NgModule({
    declarations: [
        NgmoduleProvidersComponent
    ],
    providers: [
        NgmoduleProvidersService,
    ],
    imports: [
        CommonModule,
        /**
         * 导入了过渡的NgModule
         */
        NgmoduleProvidersResolveModule,
        NgmoduleProvidersRoutingModule
    ]
})
export class NgmoduleProvidersModule {
}

1.2 Component (component) stage injector

       Stage injector assembly, each component is an injector. At the component level by injecting implanter. Examples of such lower component instance, or the components can use the service (provided, of course, we can also use only the current component, subcomponent can not be used. This involves the difference between the providers and the viewProviders). Service Component injector provided with a limited life cycle. Each new instance of the component will get their share of the service instance. When the destruction component instance, a service instance will be destroyed at the same time. So the component level of services and components are bundled together. Creating disappear along with.

       Let's look at the use of component-level injector through a simple example.

First define a ComponentInjectService service.

import { Injectable } from '@angular/core';

/**
 * 当前服务在组件里面使用,会在需要使用的组件里面注入
 */
@Injectable()
export class ComponentInjectService {

  constructor() { }
}

Is then injected inside the assembly

import {ComponentInjectService} from './component-inject.service';

@Component({
    selector: 'app-ngmodule-providers',
    templateUrl: './ngmodule-providers.component.html',
    styleUrls: ['./ngmodule-providers.component.less'],
    providers: [ComponentInjectService],   // providers提供的服务在当前组件和子组件都可以使用
    // viewProviders: [ComponentInjectService], // viewProviders提供的服务在当前组件使用
})
export class NgmoduleProvidersComponent implements OnInit {
    
    constructor(private service: ComponentInjectService) {
    }

    ngOnInit() {
    }

}

Second, the provider (Provider)

       All of the above example code, when we injected into the inside of the injector and services, using the most simple way TypeProvider, it is most used way. Whether inside or @Component @NgModule decorator decorator inside. metadata providers which are directly written service class. Similar to the following code.

@NgModule({
    ...
    providers: [
        NgmoduleProvidersService,
    ],
    ...
})

providers objects in the code above is a Provider (provider) array (current injectors need to inject the dependent objects), when injected into the injector in service we must also specify these providers, or injectors do not know how to create this service. Angular system we create a way to describe Token-related dependent objects linked by the Provider.

      Provider is used to describe short-dependent manner to create objects associated with a Token. When we get when using Token dependent objects associated with it even, according to DI will create mode has been set to automatically create dependent objects to the user and returns to the DI system. We do not need too middle of the process. We just need to know which Token corresponds to which (or what) service just fine. To get to the corresponding service by Token. So we focus on Povider need to know two things: Token, Token way to create the corresponding object.

2.1 Povider Token

      Token is used to identify the role of dependent objects, Token value may be Type, InjectionToken, OpaqueToken class instance or a string. Usually we do not recommend the use of string, because there is the possibility of naming conflicts if a string is relatively high.

You can simply think that Token is dependent on the object key. When we need to use the dependent objects we can find the dependent objects by this key.

2.2 way to create objects

      It gives way to create dependent objects, the injector in order to know how to create objects. There are several ways Provider: TypeProvider, ValueProvider, ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, any [].

export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | ExistingProvider | FactoryProvider | any[];

ConstructorProvider this way, we will not consider, I was in the scene is not found using this approach.

2.2.1 TypeProvider

export interface TypeProvider extends Type<any> {
}

      TypeProvider used to tell Injector (injector), using the given Type object is created and is given Token Type. This is also a way most of us use. For example, as follows. It is TypeProvider methods used.

@NgModule({
  ...
  providers: [NgmoduleProvidersService], // NgmoduleProvidersService是我们定义的服务,TypeProvider方式
})

2.2.2 ClassProvider

      ClassProvider used to tell Injector (injector), the object corresponding to the object is created useClass designated Type Token is corresponding.

export interface ClassSansProvider {
    /**
     * token生成对象对应的class.
     * 用该class生成服务对象
     */
    useClass: Type<any>;
}

export interface ClassProvider extends ClassSansProvider {
    /**
     * 用于设置与依赖对象关联的Token值,Token值可能是Type、InjectionToken、OpaqueToken的实例或字符串
     */
    provide: any;
    /**
     * 用于标识是否multiple providers,若是multiple类型,则返回与Token关联的依赖对象列表
     * 简单来说如果multi是true的话,通过provide(Token)获取的依赖对象是一个列表。
     * 同一个Token可以注入多个服务
     */
    multi?: boolean;
}

      Simple to use

export const TOKEN_MODULE_CLASS_PROVIDER = new InjectionToken<any>('TOKEN_MODULE_CLASS_PROVIDER');
// ModuleClassProviderService类是我们依赖对象

@NgModule({
    ...
    providers: [
        {
            provide: TOKEN_MODULE_CLASS_PROVIDER, useClass: ModuleClassProviderService
        }
    ],
    ...
})
export class ClassProviderModule {
}

2.2.3 ValueProvider

      ValueProvider tells Injector (injector), the value specified useValue (may be a particular object may be value string, number, etc. and the like) is dependent objects Token.

export interface ValueSansProvider {
  /**
   * 需要注入的值
   */
  useValue: any;
}

export interface ValueProvider extends ValueSansProvider {
  /**
   * 用于设置与依赖对象关联的Token值,Token值可能是Type、InjectionToken、OpaqueToken的实例或字符串
   */
  provide: any;
  /**
   * 用于标识是否multiple providers,若是multiple类型,则返回与Token关联的依赖对象列表
   * 简单来说如果multi是true的话,通过provide(Token)获取的依赖对象是一个列表。
   * 同一个Token可以注入多个服务
   */
  multi?: boolean;
}

      A simple example.

export const TOKEN_MODULE_CONFIG = new InjectionToken<Config>('TOKEN_MODULE_CONFIG');

/**
 * Config是我们自定义的一个配置对象
 */
const config = new Config();
config.version = '1.1.2';

@NgModule({
    ...
    providers: [
        {provide: TOKEN_MODULE_CONFIG, useValue: config},
    ],
    ...
})
export class ValueProviderModule {
}

2.2.4 FactoryProvider

      FactoryProvider tells Injector (injector), by calling the function corresponding to useFactory, Token returned corresponding dependent objects.

export interface FactorySansProvider {
  /**
   * 用于创建对象的工厂函数
   */
  useFactory: Function;
  /**
   * 依赖对象列表(你也可以简单的认为是创建对象构造函数里面需要的依赖对象)
   */
  deps?: any[];
}

export interface FactoryProvider extends FactorySansProvider {
  /**
   * 用于设置与依赖对象关联的Token值,Token值可能是Type、InjectionToken、OpaqueToken的实例或字符串
   */
  provide: any;
  /**
   * 用于标识是否multiple providers,若是multiple类型,则返回与Token关联的依赖对象列表
   * 简单来说如果multi是true的话,通过provide(Token)获取的依赖对象是一个列表。
   * 同一个Token可以注入多个服务
   */
  multi?: boolean;
}

      useFactory corresponds to a function, the function requires the object is provided by deps, deps array is a Token.

// TOKEN
export const TOKEN_FACTORY_MODULE_DEPS = new InjectionToken<ModuleFactoryProviderService>('TOKEN_FACTORY_MODULE_DEPS');
export const TOKEN_FACTORY_MODULE = new InjectionToken<ModuleFactoryProviderService>('TOKEN_FACTORY_MODULE');


/**
 * 创建ModuleFactoryProviderService对象,
 * 该对象依赖另一个服务,通过deps提供
 */
function moduleServiceFactory(initValue) {
    return new ModuleFactoryProviderService(initValue);
}

@NgModule({
    ...
    providers: [
        { // 创建TOKEN_FACTORY_MODULE对应的服务时候,需要依赖的值
            provide: TOKEN_FACTORY_MODULE_DEPS,
            useValue: 'initValue'
        },
        {
            provide: TOKEN_FACTORY_MODULE,
            useFactory: moduleServiceFactory,
            deps: [TOKEN_FACTORY_MODULE_DEPS]
        }
    ],
    ...
})
export class FactoryProviderModule {
}

2.2.5 ExistingProvider

      When ExistingProvider used to tell Injector (injector), I want to get Token (provide) the corresponding object, use useExisting (Token) corresponding to the object.

Always remember useExisting corresponding value is also a Token.

export interface ExistingSansProvider {
  /**
   * 已经存在的 `token`  (等价于 `injector.get(useExisting)`)
   */
  useExisting: any;
}

export interface ExistingProvider extends ExistingSansProvider {
  /**
   * 用于设置与依赖对象关联的Token值,Token值可能是Type、InjectionToken、OpaqueToken的实例或字符串
   */
  provide: Type<any>;
  /**
   * 用于标识是否multiple providers,若是multiple类型,则返回与Token关联的依赖对象列表
   * 简单来说如果multi是true的话,通过provide(Token)获取的依赖对象是一个列表。
   * 同一个Token可以注入多个服务
   */
  multi?: boolean;
}

      Examples of code.

@NgModule({
    ...
    providers: [
        ModuleExistingProviderServiceExtended, // 我们先通过TypeProvider的方式注入了ModuleExistingProviderServiceExtended
        {provide: ModuleExistingProviderService, useExisting: ModuleExistingProviderServiceExtended}
    ],
    ...
})
export class ExistingProviderModule {
}

Third, access to dependent objects

      Through the above explanation, we already know how the injection of the corresponding dependent objects specified by the provider of the injector inside. If we want to place a specified (usually inside the component) using dependent objects, you have to first get the object. Next we have a lot to say how to get the object.

      When injected services by providers (providers), we gave each service set the Token (Provider inside provide a value corresponding to the object). TypeProvider exception, in fact TypeProvider although not explicitly stated that Token. In fact, processing, Token Type is set inside the TypeProvider.

      We conclude that there are three ways to get dependent objects:

3.1 constructor obtained by @Inject

      @Inject decorator acquired by the specified dependent objects. @Inject parameter needs to be acquired is dependent objects corresponding Token.

    /**
     * 通过@Inject装饰器获取Token对应依赖的对象
     */
    constructor(@Inject(TOKEN_MODULE_CLASS_PROVIDER) private service: ModuleClassProviderService) {
    }

3.2 obtained by Injector.get (Token)

      In the first constructor injection Injector incoming objects, the object is then acquired by Injector.get (Token). The same parameters are also dependent objects corresponding Token.

    service: ModuleClassProviderService;

    /**
     * 借助Injector服务来获取Token对应的服务
     */
    constructor(private injector: Injector) {
       this.service = injector.get(TOKEN_MODULE_CLASS_PROVIDER);
    }

3.3 obtained by the constructor Type

      Type directly obtained by the constructor, this acquisition mode has a premise. Service must be provided by way of TypeProvider.

    constructor(private service: ModuleClassProviderService) {
    }

Four, Provider of multi

      Mentioned above provider (Provider) when there were frequent multi. Token denotes a multi corresponding to the same service may be plural. When using the multi. Token dependent services acquired by the time a service array. In fact, it is also well understood. For example, the network interceptor. Token is allowed to have more than the same service. Each interceptor do different logic.


      Finally, the article gives the example code Download involved https://github.com/tuacy/angular-inject , and we rely on the use of injection of Angular make a brief summary. Angular which use dependency injection steps:

  • 1. Define business logic dependent objects.

It is the definition of dependent objects service class, service class to determine what needs to be dry.

  • 2. We rely on a clear target range of action.

Determine the injector, the injector is NgModule it, in real time Component injector.

  • 3. Token determined dependent objects.

Dependent objects are acquired by the Token to acquire.

  • 4. dependency determination target provider.

Provider that way, TypeProvider, or is ValueProvider it and so on.

  • 5. Where required dependent objects acquired dependent objects.

Guess you like

Origin blog.csdn.net/wuyuxing24/article/details/90729785