简体   繁体   English

反射式注入器与角度注入器之间的区别

[英]Difference between Reflective Injector and Injector in Angular

I tried to create dependency explictly using two ways. 我试图使用两种方法显式创建依赖关系。 Both are almost same but I am little bit confusing what's the advantage of using Reflective Injector over the normal Injector and which one is recommended way? 两者几乎相同,但我有点混淆使用反射式喷射器比普通喷射器的优势,推荐哪种方式?

Using Injector 使用喷油器

import { Injector } from '@angular/core';
constructor( private injector: Injector) {
    this.myService = this.injector.get(MyService);
  }

Using Reflective Injector 使用反射式喷油器

import { ReflectiveInjector } from '@angular/core';
 constructor() {

       var injector= ReflectiveInjector.resolveAndCreate([MyService]);
       let myservice=injector.get(MyService);
     }

An injector is a container with providers/services. 注射器是带有提供者/服务的容器。 It implements one method get and returns an instance of the service. 它实现一种方法get并返回服务的实例。 Let's implement the most basic version of the injector in JS pseudocode: 让我们用JS伪代码实现注入器的最基本版本:

class ReflectiveInjector {
   providers = [];

   static resolveAndCreate(providers) {
     providers.forEach((provider)=>{
         providers.push({token: provider.provide, instance: new provider.useClass})
     });  
  )}

  get(dep) {
     return providers.find((provider)=>{ return provider.token === token });
  }
}

Now, we need to create the instance of the injector before we can use it. 现在,我们需要先创建注入器的实例,然后才能使用它。 And when we create it we define providers: 当我们创建它时,我们定义了提供者:

const existingInjector = ReflectiveInjector.resolveAndCreate([{provide: A, useClass: A }]);
const AInstance = existingInjector.get(A);

So you see in order to use an injector it has to be created first. 因此,您看到为了使用注射器,必须首先创建它。 It doesn't have any specific methods that allow adding providers so after it's created we can't add any new providers to it. 它没有允许添加提供者的任何特定方法,因此在创建之后,我们无法向其添加任何新的提供者。

The injector you inject into the component constructor is already created by Angular. Angular 已经创建了您注入到组件构造函数中的注入器。 You can't add anything to it and can query only the providers already defined on it. 您不能向其添加任何内容,并且只能查询已经在其上定义的提供程序。 If you need to provide B class, you can't do that with existing injector. 如果需要提供B级,则无法使用现有的进样器进行。 You need a new one. 您需要一个新的。 And this is where ReflectiveInjector class comes in. It allows you to add new providers by creating a new instance of the injector and registering new providers. 这就是ReflectiveInjector类的来源。它允许您通过创建注射器的新实例并注册新的提供程序来添加新的提供程序。 And the good part is that it can also setup an injector chain. 好处是它还可以设置喷射器链。

Let's a bit modify resolveAndCreate and get methods to allow chaining injectors: 让我们修改resolveAndCreateget允许链接注入器的方法:

class ReflectiveInjector {
   providers = [];
   parent;

   static resolveAndCreate(providers, parent) {
       this.parent = parent;
   ...
   }

   get(dep) {
       let found = providers.find((provider)=>{ return provider.token === token });
       if (!found && parent) {
            found = parent.get(dep);
       }

       return found;
   }

So now the only that left is to use it: 所以现在剩下的就是使用它:

// passing existingInjector as a parent
const childInjector = ReflectiveInjector.resolveAndCreate([{provide: B, useClass: B }], i);
const AInstance = childInjector.get(A);
const BInstance = childInjector.get(B);

Now, suppose someone might want to get access to our existingInjector . 现在,假设有人可能想要访问我们existingInjector We need a token to use to get this existing injector. 我们需要一个令牌来获取这个现有的注射器。 Let's define this token like this: 让我们这样定义令牌:

abstract class Injector {}

and let's write a function that will get the existing injector: 并编写一个可以获取现有注射器的函数:

function resolveDependency(token) {
   if (token === Injector) {
       return existingInjector;
   }
}

And now suppose that Angular when executes the constructor of the component, uses the tokens you specified to get dependencies and passes them to the resolveDependency function. 现在假设Angular在执行组件的构造函数,使用您指定的标记获取依赖项并将它们传递给resolveDependency函数时。 So you write like this: 所以你这样写:

// the Injector here is a reference to our abstract Injector class and simply used as a token
MyComp {
   constructor(private injector: Injector) { ...  }
}

And the token here is Injector which as I said is passed into the resolveDependency function and the existing injector is returned. 这里的令牌是Injector ,正如我所说的,它被传递到resolveDependency函数中,并返回现有的注射器。

Angular will only create an instance once for each provider. Angular只会为每个提供程序创建一次实例。 Therefore, if you want to use the Injector as a factory you can't. 因此,如果您想将Injector用作工厂,则不能。 You have to create a new injector for each new instance of an object you need. 您必须为所需对象的每个新实例创建一个新的注射器。

So let's say we have a service class called ReportsService and it needs to create Record objects at run-time, but you want Record to be injectable. 假设我们有一个名为ReportsService的服务类,它需要在运行时创建Record对象,但是您希望Record是可注入的。

Here's a class that we need to create multiple instances of. 这是我们需要创建其多个实例的类。

@Injectable() // does not have to be defined in ngModule
export class Record {
      // just an example dependency that ReportsService doesn't know about
      public constructor(http: Http) {
      }
}

Here's the service that will create the above class. 这是将创建上述类的服务。

@Injectable() // exported via ngModule
export class RecordsService {
      // we need injector as parent otherwise Record class will not find Http
      public constructor(private parent: Injector) {
      }

      // factory method
      public createRecord(): Record {
          // creates a new injector that knows about Record but extends existing injector
          let injector = ReflectiveInjector.resolveAndCreate([
             {provide: Record, useClass: Record}
          ], this.parent);

          // first call, object is created
          let record = injector.get(Record);
          // just an example
          let example = injector.get(Record);
          // will output true
          console.log(record === example ? 'true' : 'false');

          return record;
     }
}

Now a new instance of Record is great, but what's the point if each object is exactly the same. 现在,一个新的Record实例很棒,但是如果每个对象都完全相同又有什么用呢? So we need to inject parameters for Record . 因此,我们需要为Record注入参数。 We'll create a token for a string value. 我们将为字符串值创建令牌。

export const RECORD_URL = new InjectionToken<string>('record-url');

@Injectable()
export class Record {
      public constructor(http: Http, @Inject(RECORD_URL) url: string) {
      }
}

Now update the create function 现在更新创建功能

 public createRecord(url: string): Record {
     let injector = ReflectiveInjector.resolveAndCreate([
        {provide: Record, useClass: Record},
        {provide: RECORD_URL, useValue: url
     ], this.parent);
     return injector.get(Record);
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM