Skip to content

前言

上班中碰到需要写@Sub @Pub的来实现在不同节点发布订阅事件,仔细看了官方的源码

动态注册Module最佳方案

  1. 使用useValue将options注入到Service中
ts
// Config.module.ts
import { ConfigService } from './config.service.ts'

@Module({})
export class ConfigModule {
    static register(options) {
        return {
            module: ConfigModule,
            providers: [
                {
                    provide: "CONFIG_OPTIONS",
                    useValue: options
                },
                ConfigService
            ],
        }
    }
}

// config.service.ts
@Injectable()
export class ConfigService {
    constructor(@Inject('CONFIG_OPTIONS') options) {
        // 
    }
}
  1. 使用ConfigurableModuleBuilder构建动态模块
ts
const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ConfigurableModuleBuilder<ConfigModuleOptions>().setExtra({
    isGlobal: true
}, (def, extra) =>({
    ...def,
    global: extra
})).build()

@Module({
    providers: [ConfigService],
})
export class SomeModule extends ConfigurableModuleClass {}

依赖的socpe,scope的冒泡性,冒泡性的使用场景

依赖有3种scope,全局单例,请求单例和瞬时。默认是全局单例,通过contextId实现请求单例。每次请求时会创建一个contextId,后续会根据contextId检索请求单例。瞬时是每次注入时都创建一个新的示例,特别注意是注入时才创建,比如在多个service中注入时,每个service 会创建一次。

依赖的冒泡性

如果一个依赖是请求,那么引入的这个依赖的服务也会冒泡变成请求。所以需要注意对 请求范围提供者的注入,可能会导致整个controller变成请求范围

依赖的分组

对于请求范围的依赖注入,每次请求都会创建实例,如果能够对同一个用户所有的请求共享实例,或者多个用户共享实例。需要用到依赖分组

  1. 首先标记请求,制定什么是常见的请求,什么是特殊需要分组的请求。
ts
export class AggregateByTenantContextIdStrategy implements ContextIdStrategy {
  attach(contextId: ContextId, request: Request) {
    const tenantId = request.headers['x-tenant-id'] as string;
    let tenantSubTreeId: ContextId;

    if (tenants.has(tenantId)) {
      tenantSubTreeId = tenants.get(tenantId);
    } else {
      tenantSubTreeId = ContextIdFactory.create();
      tenants.set(tenantId, tenantSubTreeId);
    }

    // If tree is not durable, return the original "contextId" object
    return (info: HostComponentInfo) =>
      info.isTreeDurable ? tenantSubTreeId : contextId;
  }
}
  1. 对依赖进行标记,添加durable: true,将依赖提升成分组依赖

分组依赖也是具有冒泡性,如果一个依赖是分组依赖, 依赖该依赖的服务也会分组。

ModuleRef

注入当前module,可以获取当前模块中的依赖,通过token手动注入依赖或者全局依赖。

ModuleRef.get 和 ModuleRef.resolve

获取module中依赖,前者获取单例,后者获取请求范围或者瞬时单例, 通过相同的ContextId 可以保证创建相同的依赖。

ModuleRef.create

直接创建依赖

Module.registerRequestByContextId

注册一个请求,把请求和contextId绑定,用于后续的@Req 等。

discoverService 发现服务

查询所有已经注册的service和controller,通过DiscoveryService.createDecorator 创建的装饰器,还能被快速的查询。

ts
export const FeatureFlag = DiscoveryService.createDecorator();

this.discoveryService.getMetadataByDecorator(FeatureFlag, item)

必须掌握的生命周期

  1. 传入请求

  2. 中间件

    1. 全局绑定的中间件
    2. 模块绑定中间件
  3. 卫兵

    1. 全局守卫
    2. 控制器守卫
    3. 路由守卫
  4. 拦截器(预控制器)

    1. 全局拦截器
    2. 控制器拦截器
    3. 路由拦截器
  5. 管道

    1. 全局管道
    2. 控制器管道
    3. 管道布线
    4. 路由参数管道
  6. 控制器(方法处理程序)

  7. 服务(如果存在)

  8. 拦截器(请求后)

    1. 路由拦截器
    2. 控制器拦截器
    3. 全局拦截器
  9. 异常过滤器

    1. 路线
    2. 控制器
    3. 全球
  10. 服务器响应

持久提供程序

Reflecor如何发现元数据,和原生的metadata定义有什么好处。

DI 树和 DI子树

按照模块划分子树

MetadataScanner 用法

已经被reflector 取代,开发者不需要关注