Fundamentals

发现服务

了解如何使用 DiscoveryService 在运行时动态发现和检索应用程序中的提供者、控制器和模块

发现服务

有时,您可能需要在运行时检索有关已注册的提供者控制器模块的信息。DiscoveryService 提供了一个 API 来查询这些信息。

开始使用

要启用 DiscoveryService,请将其导入到您的模块中:

@@filename(app.module)
import { Module } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';

@Module({
  imports: [DiscoveryModule],
})
export class AppModule {}

提示 DiscoveryModule 是从 @nestjs/core 包导出的。

现在您可以在任何类中注入 DiscoveryService

@@filename()
export class ExampleProvider {
  constructor(private discoveryService: DiscoveryService) {}
}
@@switch
export class ExampleProvider {
  constructor(discoveryService) {
    this.discoveryService = discoveryService;
  }
}

检索实例

要检索已注册的提供者和控制器,请使用 getProviders()getControllers() 方法。

@@filename()
getProviders(): InstanceWrapper[];
getControllers(): InstanceWrapper[];
@@switch
getProviders();
getControllers();

这两个方法都返回 InstanceWrapper[]InstanceWrapper 是一个包装器,它保存有关实例的信息,如:

  • name - 类名
  • metatype - 类引用
  • instance - 在这种情况下,实例化的实例(对于单例作用域的提供者,它将始终是 null
  • isResolved - 指示提供者是否已实例化
  • scope - 提供者的作用域
  • durable - 指示提供者是否持久
  • host - 提供者所属的模块

提示 InstanceWrapper 是从 @nestjs/core 包导出的。

例如:

@@filename()
export class ExampleProvider implements OnModuleInit {
  constructor(private discoveryService: DiscoveryService) {}

  onModuleInit() {
    const providers = this.discoveryService.getProviders();
    const controllers = this.discoveryService.getControllers();

    // 查找特定的提供者
    const exampleProvider = providers.find(
      wrapper => wrapper.name === 'ExampleProvider'
    );
    console.log('ExampleProvider instance:', exampleProvider.instance);
  }
}
@@switch
export class ExampleProvider {
  constructor(discoveryService) {
    this.discoveryService = discoveryService;
  }

  onModuleInit() {
    const providers = this.discoveryService.getProviders();
    const controllers = this.discoveryService.getControllers();

    // 查找特定的提供者
    const exampleProvider = providers.find(
      wrapper => wrapper.name === 'ExampleProvider'
    );
    console.log('ExampleProvider instance:', exampleProvider.instance);
  }
}

过滤结果

getProviders()getControllers() 方法接受一个可选的选项对象,允许您过滤结果:

@@filename()
interface DiscoveryOptions {
  metadataKey?: string | symbol;
  filter?: (wrapper: InstanceWrapper) => boolean;
  include?: Function[];
}
@@switch
interface DiscoveryOptions {
  metadataKey;
  filter;
  include;
}
  • metadataKey - 元数据键,用于过滤具有特定元数据的实例
  • filter - 一个函数,用于过滤实例包装器
  • include - 一个模块数组,用于仅包含来自特定模块的实例

例如,要获取所有具有 @Injectable() 装饰器的提供者:

@@filename()
const injectableProviders = this.discoveryService.getProviders({
  filter: (wrapper) => wrapper.metatype && Reflect.hasMetadata('__injectable__', wrapper.metatype)
});
@@switch
const injectableProviders = this.discoveryService.getProviders({
  filter: (wrapper) => wrapper.metatype && Reflect.hasMetadata('__injectable__', wrapper.metatype)
});

或者,要获取来自特定模块的所有控制器:

@@filename()
const appControllers = this.discoveryService.getControllers({
  include: [AppModule]
});
@@switch
const appControllers = this.discoveryService.getControllers({
  include: [AppModule]
});

检索模块

要检索已注册的模块,请使用 getModules() 方法:

@@filename()
getModules(): Module[];
@@switch
getModules();

此方法返回 Module[]Module 对象包含有关模块的信息,如:

  • metatype - 模块类引用
  • scope - 模块的作用域
  • distance - 模块在模块图中的深度
  • providers - 在此模块中注册的提供者
  • controllers - 在此模块中注册的控制器
  • imports - 此模块导入的模块
  • exports - 此模块导出的提供者

例如:

@@filename()
export class ExampleProvider implements OnModuleInit {
  constructor(private discoveryService: DiscoveryService) {}

  onModuleInit() {
    const modules = this.discoveryService.getModules();
    
    modules.forEach(module => {
      console.log(`Module: ${module.metatype.name}`);
      console.log(`Providers: ${module.providers.size}`);
      console.log(`Controllers: ${module.controllers.size}`);
    });
  }
}
@@switch
export class ExampleProvider {
  constructor(discoveryService) {
    this.discoveryService = discoveryService;
  }

  onModuleInit() {
    const modules = this.discoveryService.getModules();
    
    modules.forEach(module => {
      console.log(`Module: ${module.metatype.name}`);
      console.log(`Providers: ${module.providers.size}`);
      console.log(`Controllers: ${module.controllers.size}`);
    });
  }
}

实际用例

DiscoveryService 在以下场景中特别有用:

  1. 动态路由注册 - 基于装饰器或元数据动态注册路由
  2. 插件系统 - 发现和加载插件
  3. 自动配置 - 基于可用的提供者自动配置服务
  4. 监控和调试 - 检查应用程序的结构和状态

例如,创建一个自动发现所有带有特定装饰器的服务:

@@filename()
// 自定义装饰器
export const AutoRegister = (metadata: any) => SetMetadata('auto-register', metadata);

// 发现服务
export class AutoDiscoveryService implements OnModuleInit {
  constructor(private discoveryService: DiscoveryService) {}

  onModuleInit() {
    const providers = this.discoveryService.getProviders({
      metadataKey: 'auto-register'
    });

    providers.forEach(wrapper => {
      const metadata = Reflect.getMetadata('auto-register', wrapper.metatype);
      console.log(`Auto-registering: ${wrapper.name}`, metadata);
      // 执行自动注册逻辑
    });
  }
}
@@switch
// 自定义装饰器
export const AutoRegister = (metadata) => SetMetadata('auto-register', metadata);

// 发现服务
export class AutoDiscoveryService {
  constructor(discoveryService) {
    this.discoveryService = discoveryService;
  }

  onModuleInit() {
    const providers = this.discoveryService.getProviders({
      metadataKey: 'auto-register'
    });

    providers.forEach(wrapper => {
      const metadata = Reflect.getMetadata('auto-register', wrapper.metatype);
      console.log(`Auto-registering: ${wrapper.name}`, metadata);
      // 执行自动注册逻辑
    });
  }
}

注意 DiscoveryService 只能在模块初始化后使用(即在 OnModuleInit 生命周期钩子中或之后)。在此之前,应用程序的依赖图可能尚未完全构建。