NestJS: внедрение зависимостей и регистрация поставщика

#node.js #dependency-injection #service #nestjs

#node.js #внедрение зависимостей #Обслуживание #nestjs

Вопрос:

Кто-нибудь может помочь мне понять основы DI Nest, мой вопрос:

«Возможно ли иметь класс обслуживания без @Injectable аннотации, а также этот класс не принадлежит ни одному модулю?» Я видел в Интернете пример, подобный приведенному ниже:

Этот класс существует в общей папке:

 export class NotificationService {
  constructor(
    @Inject(Logger) private readonly logger: LoggerService,
    private readonly appConfigService: AppConfigService,
    @Inject(HttpService) private readonly httpService: HttpService
  ) {}
 
  async sendNotification(msg: string) {
   ....
  } 
}
 

А затем он был зарегистрирован в другом модуле в массиве поставщиков:

 import { Module, Logger, forwardRef, HttpModule } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { NotificationService } from '../../commons/notification/notification.service';
 
@Module({
    imports: [
        ...
    ],
    controllers: [InvoiceController],
    providers: [
        InvoiceService,
        NotificationService,
        Logger],
    exports: [InvoiceService]
})
export class InvoiceModule { }
 

Затем он был введен в метод конструктора другой службы

 @Injectable()
export class InvoiceService {
 
    constructor(
        @Inject(Logger) private readonly logger: LoggerService,
        private readonly notificationService: NotificationService) { }
 
...
}
 

Это работает нормально, но я не знаю почему. Почему служба уведомлений была введена правильно без добавления @Injectable и без модуля импорта?

Ответ №1:

Итак, давайте разберемся, что на самом деле происходит с @Injectable() декоратором.

Обычно мы используем декораторы для установки метаданных о классе, параметре, методе или свойстве, которые мы украшаем, или мы используем их, чтобы каким-то образом изменить метод (если метод декоратор) или свойство (декоратор свойств) с помощью дескрипторов. В случае @Injectable() с мы на самом деле не делаем ни того, ни другого. Конечно, мы устанавливаем метаданные области видимости, но, похоже, на самом деле это не устанавливает никаких метаданных о «Эй, этот класс можно вводить через Nest framework». Это потому, что @Injectable() то, что на самом деле настраивает нас, — это специальное свойство компилятора tsconfig and tsc , emitDecoratorMetadata свойство. С помощью этого свойства typescript добавит множество дополнительных функций в начале и в конце файла. В целом эти функции выглядят следующим образом

 var __decorate = (this amp;amp; this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" amp;amp; typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 amp;amp; r amp;amp; Object.defineProperty(target, key, r), r;
};
var __metadata = (this amp;amp; this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" amp;amp; typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
 

и

 DelegatorService = __decorate([
    common_1.Injectable(),
    __metadata("design:paramtypes", [http_interceptor_service_1.HttpInterceptorService,
        websocket_interceptor_service_1.WebsocketInterceptorService,
        rpc_interceptor_service_1.RpcInterceptorService,
        gql_interceptor_service_1.GqlInterceptorService])
], DelegatorService);
 

Это очень важная часть, потому что это "design:paramtypes" на самом деле то, что Nest читает, когда дело доходит до определения того, что вводить.

Эти метаданные доступны только тогда, когда декоратор используется где-либо в классе, и на самом деле была дискуссия eslint-typescript о метаданных и о том, как import type их нарушать, а также недавний PR, который действительно попадает в сорняки вещей.

Я привожу все это, чтобы сказать, что, поскольку у вас есть @Inject() конструктор, он на самом @Injectable() деле является посторонним и не является необходимым, если только вы не собираетесь устанавливать метаданные уровня области. Метаданные типа уже будут отправлены, так что это объясняет, почему вам не нужно @Injectable() (хотя я все еще думаю, что это хорошая идея, потому что она обеспечивает четкое намерение).

Теперь о том, почему внедрение работает должным образом, я обещаю, что это менее сложно: вы добавили NotificationsService в InvoiceModule « NotificationsService InvoiceModule NotificationsService для поставщиков array. This tells Nest that any service inside of has access to «, чтобы его можно было без проблем ввести здесь.

Комментарии:

1. Спасибо за ваше объяснение, поэтому я делаю вывод, что класс, подобный NotificationService, может существовать без принадлежности к какому-либо модулю, который его экспортирует, и, во-вторых, используя register provider , как в этом случае, Nest за кулисами создает новый экземпляр NotificationService, нет необходимости использовать значение: new NotificationService