【问题标题】:Difference between Reflective Injector and Injector in AngularAngular 中反射式喷油器和喷油器的区别
【发布时间】:2017-08-02 00:06:35
【问题描述】:

我尝试使用两种方式显式创建依赖项。两者几乎相同,但我有点困惑使用反射式注射器与普通注射器相比有什么优势以及推荐哪种方式?

使用注射器

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

【问题讨论】:

  • Injector 是一个容器,不能创建新的提供者。 ReflectiveInjector 具有允许您在运行时定义新提供程序的工厂方法(即创建新的子注入器)。
  • @ThinkingMedia 子注入器意味着子依赖?
  • @ThinkingMedia 我有一个没有子依赖的服务。我想显式地创建它的一个实例。推荐哪种方法?

标签: angular dependency-injection


【解决方案1】:

注入器是带有提供者/服务的容器。它实现了一个方法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函数并返回现有的注入器。

【讨论】:

  • 我认为第一个类名是 Injector 而不是 ReflectiveInjector。你能确认一下吗?
  • 不,它是 ReflectiveInjector,让我补充一些说明
  • 好的..最后几行为我消除了困惑。谢谢。我还有一个疑问。当我使用反射注入器时,我需要在我的模块中注册服务吗?
  • 问题是你打算如何处理你创建的反射注入器实例?
  • @JEMI 如果您能够在模块中注册它们,则根本不应该使用 ReflectiveInjector。这是在极少数情况下应该动态完成的。它是 AngularJS 中angular.inject 的直接对应物,angular.inject 在 99% 的时间里被误用。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多