diff --git a/README.md b/README.md index 78d51f1b..9defd8c4 100644 --- a/README.md +++ b/README.md @@ -934,7 +934,7 @@ import { EggObjectFactory } from '@eggjs/tegg'; export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; - + async hello(): Promise { const helloImpl = await this.eggObjectFactory.getEggObject(AbstractHello, HelloType.BAR); return helloImpl.hello(); @@ -942,6 +942,27 @@ export class HelloService { } ``` +动态获取多个实现,通过 for/await 循环获得实例。 + +```ts +import { EggObjectFactory } from '@eggjs/tegg'; + +@ContextProto() +export class HelloService { + @Inject() + private readonly eggObjectFactory: EggObjectFactory; + + async hello(): Promise { + const helloImpls = await this.eggObjectFactory.getEggObjects(AbstractHello); + const messages = []; + for await (const helloImpl of helloImpls) { + messages.push(helloImpl.hello()); + } + return messages; + } +} +``` + ### 配置项目 module 一般情况下,无需在项目中手动声明包含了哪些 module,tegg 会自动在项目目录下进行扫描。但是会存在一些特殊的情况,tegg 无法扫描到,比如说 module 是通过 npm 包的方式来发布。因此 tegg 支持通过 `config/module.json` 的方式来声明包含了哪些 module。 diff --git a/core/dynamic-inject-runtime/src/EggObjectFactory.ts b/core/dynamic-inject-runtime/src/EggObjectFactory.ts index 136a7d7d..c01f2034 100644 --- a/core/dynamic-inject-runtime/src/EggObjectFactory.ts +++ b/core/dynamic-inject-runtime/src/EggObjectFactory.ts @@ -1,6 +1,11 @@ import type { EggContainerFactory } from '@eggjs/tegg-runtime'; -import { EggAbstractClazz, EggObjectFactory as IEggObjectFactory, QualifierImplUtil } from '@eggjs/tegg-dynamic-inject'; import { AccessLevel, PrototypeUtil, QualifierValue, SingletonProto } from '@eggjs/core-decorator'; +import { + EggAbstractClazz, + EggObjectFactory as IEggObjectFactory, + QualifierImplUtil, +} from '@eggjs/tegg-dynamic-inject'; + import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from './EggObjectFactoryPrototype'; @SingletonProto({ @@ -23,4 +28,26 @@ export class EggObjectFactory implements IEggObjectFactory { const eggObject = await this.eggContainerFactory.getOrCreateEggObject(protoObj, protoObj.name); return eggObject.obj as T; } + + async getEggObjects(abstractClazz: EggAbstractClazz) { + const implClazzMap = QualifierImplUtil.getQualifierImpMap(abstractClazz); + const getEggObject = this.getEggObject.bind(this); + const qualifierValues = Array.from(implClazzMap.keys()); + + return { + [Symbol.asyncIterator]() { + return { + key: 0, + async next() { + if (this.key === qualifierValues.length) { + return { done: true } as IteratorResult; + } + + const value = await getEggObject(abstractClazz, qualifierValues[this.key++]); + return { value, done: false } as IteratorResult; + }, + }; + }, + }; + } } diff --git a/core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/HelloService.ts b/core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/HelloService.ts index fd1097b4..cfb12062 100644 --- a/core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/HelloService.ts +++ b/core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/HelloService.ts @@ -1,8 +1,9 @@ import { ContextProto, Inject } from '@eggjs/core-decorator'; -import { ContextHelloType, SingletonHelloType } from './FooType'; +import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject'; + import { AbstractContextHello } from './AbstractContextHello'; import { AbstractSingletonHello } from './AbstractSingletonHello'; -import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject'; +import { ContextHelloType, SingletonHelloType } from './FooType'; @ContextProto() export class HelloService { @@ -19,4 +20,19 @@ export class HelloService { const msgs = helloImpls.map(helloImpl => helloImpl.hello()); return msgs; } + + async sayHelloToAll(): Promise { + const singletonHellos = await this.eggObjectFactory.getEggObjects(AbstractSingletonHello); + const contextHellos = await this.eggObjectFactory.getEggObjects(AbstractContextHello); + + const msgs: string[] = []; + for await (const helloImpl of singletonHellos) { + msgs.push(helloImpl.hello()); + } + for await (const helloImpl of contextHellos) { + msgs.push(helloImpl.hello()); + } + + return msgs; + } } diff --git a/core/dynamic-inject-runtime/test/index.test.ts b/core/dynamic-inject-runtime/test/index.test.ts index 367ad530..cab6f037 100644 --- a/core/dynamic-inject-runtime/test/index.test.ts +++ b/core/dynamic-inject-runtime/test/index.test.ts @@ -1,10 +1,12 @@ +import assert from 'assert'; import path from 'path'; -import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; + import { LoadUnitFactory } from '@eggjs/tegg-metadata'; +import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; + import { EggTestContext } from '../../test-util'; -import { HelloService } from './fixtures/modules/dynamic-inject-module/HelloService'; import { CoreTestHelper } from '../../test-util/CoreTestHelper'; -import assert from 'assert'; +import { HelloService } from './fixtures/modules/dynamic-inject-module/HelloService'; describe('test/dynamic-inject-runtime.test.ts', () => { let modules: Array; @@ -47,4 +49,18 @@ describe('test/dynamic-inject-runtime.test.ts', () => { ]); }); }); + + it('should work with getAllEggObjects', async () => { + await EggTestContext.mockContext(async () => { + const helloService = await CoreTestHelper.getObject(HelloService); + const msgs = await helloService.sayHelloToAll(); + assert.deepStrictEqual(msgs, [ + 'hello, bar(singleton:0)', + 'hello, foo(singleton:0)', + 'hello, bar(context:0)', + 'hello, foo(context:0)', + ]); + }); + + }); }); diff --git a/core/dynamic-inject/src/EggObjectFactory.ts b/core/dynamic-inject/src/EggObjectFactory.ts index e63c2382..db0052b0 100644 --- a/core/dynamic-inject/src/EggObjectFactory.ts +++ b/core/dynamic-inject/src/EggObjectFactory.ts @@ -1,6 +1,8 @@ -import { EggAbstractClazz } from './typing'; import { QualifierValue } from '@eggjs/core-decorator'; +import { EggAbstractClazz } from './typing'; + export interface EggObjectFactory { getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): Promise; + getEggObjects(abstractClazz: EggAbstractClazz): Promise>; } diff --git a/core/dynamic-inject/src/QualifierImplUtil.ts b/core/dynamic-inject/src/QualifierImplUtil.ts index 5a84c540..9d473dc8 100644 --- a/core/dynamic-inject/src/QualifierImplUtil.ts +++ b/core/dynamic-inject/src/QualifierImplUtil.ts @@ -1,6 +1,7 @@ -import { EggAbstractClazz } from './typing'; import { EggProtoImplClass, MetadataUtil, QualifierValue } from '@eggjs/core-decorator'; +import { EggAbstractClazz } from './typing'; + export const QUALIFIER_IMPL_MAP = Symbol.for('EggPrototype#qualifierImplMap'); export class QualifierImplUtil { @@ -9,8 +10,13 @@ export class QualifierImplUtil { implMap.set(qualifierValue, implClazz); } - static getQualifierImp(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue):EggProtoImplClass | undefined { + static getQualifierImp(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): EggProtoImplClass | undefined { const implMap: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass); return implMap?.get(qualifierValue); } + + static getQualifierImpMap(abstractClazz: EggAbstractClazz): Map { + const implMap: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass); + return implMap || new Map(); + } }