角度反射注射器与注射器的区别

时间:2017-08-01 15:22:17

标签: angular dependency-injection

我尝试使用两种方式明确地创建依赖关系。两者几乎相同,但我有点混淆使用Reflective Injector比普通Injector有什么好处,推荐使用哪一种?

使用Injector

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

使用反光注射器

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

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

2 个答案:

答案 0 :(得分:5)

注入器是具有提供者/服务的容器。它实现了一个方法get并返回服务的实例。让我们在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 });
  }
}

现在,我们需要在使用它之前创建注入器的实例。当我们创建它时,我们定义提供者:

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

因此,为了使用注射器,您必须首先创建注射器。它没有任何允许添加提供程序的特定方法,因此在创建后我们无法向其添加任何新提供程序。

您注入到组件构造函数的注入器已由Angular创建。您无法向其添加任何内容,只能查询已在其上定义的提供程序。如果您需要提供B课程,则不能使用现有注射器。你需要一个新的。这就是ReflectiveInjector类的用武之地。它允许您通过创建注入器的新实例并注册新的提供程序来添加新的提供程序。而且很重要的是它还可以设置喷射器链。

让我们稍微修改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;
   }

所以现在唯一剩下的就是使用它:

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

现在,假设有人可能想要访问我们的existingInjector。我们需要一个令牌来获取这个现有的注入器。让我们像这样定义这个标记:

abstract class Injector {}

让我们编写一个能够获得现有注入器的函数:

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

现在假设Angular在执行组件的构造函数时,使用您指定的标记来获取依赖项并将它们传递给resolveDependency函数。所以你这样写:

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

此处的令牌是Injector,正如我所说,它被传递到resolveDependency函数并返回现有的注入器。

答案 1 :(得分:1)

Angular只会为每个提供商创建一次实例。因此,如果您想将Injector用作工厂,则不能。您必须为所需的每个对象的新实例创建一个新的注入器。

因此,假设我们有一个名为ReportsService的服务类,它需要在运行时创建Record个对象,但您希望Record可以注入。

这是我们需要创建多个实例的类。

@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) {
      }
}

这是将创建上述类的服务。

@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;
     }
}

现在,Record的一个新实例很棒,但如果每个对象完全相同,那么重点是什么。所以我们需要为Record注入参数。我们将为字符串值创建一个标记。

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

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

现在更新创建功能

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