在没有构造函数注入的情况下获取服务实例

Getting instance of service without constructor injection

我在 bootstrap 中定义了一个 @Injectable 服务。我想在不使用构造函数注入的情况下获取服务实例。我尝试使用 ReflectiveInjector.resolveAndCreate,但这似乎创建了一个新实例。

我尝试这样做的原因是我有一个由许多组件派生的基础组件。现在我需要访问一个服务,但我不想将它添加到 ctor,因为我不想在所有派生组件上注入该服务。

TLDR:我需要一个 ServiceLocator.GetInstance<T>()

更新: RC5+ 的更新代码:

是,ReflectiveInjector.resolveAndCreate() 创建一个新的未连接的注入器实例。

您可以注入 Angulars Injector 实例并使用

从中获取所需的实例
constructor(private injector:Injector) {
  injector.get(MyService);
}

您还可以将 Injector 存储在某个全局变量中,然后使用此注入器实例获取提供的实例,例如 https://github.com/angular/angular/issues/4112#issuecomment-153811572

中所述

另一种方法包括定义自定义装饰器(CustomInjectable 以设置依赖项注入的元数据:

export function CustomComponent(annotation: any) {
  return function (target: Function) {

    // DI configuration
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('design:paramtypes', parentTarget);

    Reflect.defineMetadata('design:paramtypes', parentAnnotations, target);

    // Component annotations / metadata
    var annotations = Reflect.getOwnMetadata('annotations', target);
    annotations = annotations || [];
    annotations.push(annotation);
    Reflect.defineMetadata('annotations', annotations, target);
  }
}

它将利用来自 parent 构造函数的元数据,而不是它自己的元数据。您可以在 child class:

上使用它
@Injectable()
export class SomeService {
  constructor(protected http:Http) {
  }
}

@Component()
export class BaseComponent {
  constructor(private service:SomeService) {
  }
}

@CustomComponent({
  (...)
})
export class TestComponent extends BaseComponent {
  constructor() {
    super(arguments);
  }

  test() {
    console.log('http = '+this.http);
  }
}

有关详细信息,请参阅此问题:

在使用 ngModules 的更新 Angular 中,您可以在代码的任何位置创建一个可用的变量:

app.module.ts

中添加此代码
import { Injector, NgModule } from '@angular/core';

export let AppInjector: Injector;
    
export class AppModule {
  constructor(private injector: Injector) {
    AppInjector = this.injector;
  }
}

现在,您可以使用 AppInjector 在代码的任何位置查找任何服务。

import { AppInjector } from '../app.module';

const myService = AppInjector.get(MyService);

商店服务.ts

  import { Injectable} from '@angular/core';
    
    @Injectable()
    export class StoreService {
      static isCreating: boolean = false;
      static instance: StoreService ;
    
      static getInstance() {
        if (StoreService.instance == null) {
          StoreService.isCreating = true;
          StoreService.instance = new StoreService ();
          StoreService.isCreating = false;
        }
        return StoreService.instance;
      }
      constructor() {
        if (!StoreService.isCreating) {
          throw new Error('You can\'t call new in Singleton instances! Call StoreService.getInstance() instead.');
        }
     }
    
  MyAlertMethod(){
    alert('hi);
    }
  }

.ts

//call this service directly in .ts as below:-

 StoreService.getInstance().MyAlertMethod();

运行 几次遇到这个问题后,我想出了一个很好的方法来解决这个问题,即使用 getter 和 Angular Injector 服务,而不是直接在构造函数中注入服务。这允许在引用之前构建服务时间。我的示例仅使用服务,但同样的事情可以应用于使用服务的组件,只需将 getter 放在组件中而不是示例中的 BService

我所做的是使用 getter 将服务注入 class 属性 使用 Injector class,如果 class 属性 之前尚未设置,因此该服务仅注入一次(第一次调用 getter 时)。这使得服务的使用方式与在构造函数中注入的方式基本相同,但不会出现循环引用错误。只需使用 getter this.aService。如果您尝试在 Bservice 的构造函数中使用 AService,那么唯一一次这将不起作用,那么您将遇到相同的循环引用问题,因为 Aservice 不会准备好了吗。 通过使用 getter,您可以推迟注入服务,直到您需要它。

有论点认为,AService 取决于 BService,而 BService 取决于 AService,是错误的形式,但每条规则和每种情况都有例外不同,所以我认为这是处理此问题的一种简单有效的方法。

// a.service.ts
import { Injectable } from '@angular/core';

import { BService } from './b.service';

@Injectable({
  providedIn: 'root'
})
export class AService {

  constructor(
    private bService: BService,
  ) { }

  public foo() {
    console.log('foo function in AService!');
    this.bService.bar();
  }
}
// b.service.ts
import { Injectable, Injector } from '@angular/core';

import { AService } from './a.service';


@Injectable({
  providedIn: 'root'
})
export class BService {
  // Use the getter 'aService' to use 'AService', not this variable.
  private _aService: AService;

  constructor(
    private _injector: Injector,
  ) { }

  // Use this getter to use 'AService' NOT the _aService variable.
  get aService(): AService {
    if (!this._aService) {
      this._aService = this._injector.get(AService);
    }
    return this._aService;
  }

  public bar() {
    console.log('bar function in BService!');
    this.aService.foo();
  }
}