动态函数注入

在某些场景下,我们需要函数作为某个逻辑动态执行,而 IoC 中的对象属性则都是已经创建好的,无法满足动态的逻辑需求。

比如你需要一个工厂函数,根据不同的场景返回不同的实例,也可能有一个三方包,是个函数,在业务中想要直接调用,种种的场景下,你就需要直接注入一个函数,并且在函数中拿到上下文。

标准的函数注入样例。

  1. export function contextHandler(context) {
  2. return async () => {
  3. // const xxx = context.getAsync('xxxx');
  4. return true;
  5. };
  6. }
  7. providerWrapper([
  8. {
  9. id: 'contextHandler',
  10. provider: contextHandler,
  11. }
  12. ]);

使用端。

  1. @provide()
  2. export class BaseService {
  3. @inject()
  4. contextHandler: () => boolean;
  5. }

midway 通过 providerWrapper 函数来包裹一个函数,并且指定提供的 key,供其他 IoC 对象使用。由于函数直接传递了一个 context 对象,可以轻松的通过此对象拿到所需的其他对象,而不需要管理依赖。

函数注入大多数为了创建一个简单的工厂。

  1. export function adapterFactory(context: IApplicationContext) {
  2. return async (adapterName: string) => {
  3. if (adapterName === 'google') {
  4. return await context.getAsync('googleAdapter');
  5. }
  6. if (adapterName === 'baidu') {
  7. return await context.getAsync('baiduAdapter');
  8. }
  9. // return await context.getAsync(adapterName + 'Adapter');
  10. };
  11. }
  12. providerWrapper([
  13. {
  14. id: 'adapterFactory',
  15. provider: adapterFactory,
  16. }
  17. ]);

这样在业务中,可以直接来使用了。

  1. @provide()
  2. export class BaseService {
  3. @config('adapterName')
  4. adapterName;
  5. @inject('adapterFactory')
  6. factory;
  7. adapter: Adapter;
  8. @init()
  9. async init() {
  10. this.adapter = await this.factory(this.adapterName);
  11. }
  12. }

TIP

这个函数可以是异步的 (async)。

再举个例子,比如如果应用希望自己使用 sequelize, 而 sequelize 的创建 model 的过程是个异步操作,代码就可以这么写:

  1. import { providerWrapper, IApplicationContext } from 'midway';
  2. import * as Sequelize from 'sequelize';
  3. import { Sequelize as SequelizeInstance } from 'sequelize';
  4. // 可以直接写 async 方法
  5. export async function factory(context: IApplicationContext) {
  6. const instance = await context.getAsync<SequelizeInstance>('coreDB');
  7. const UiKeyTraceModel = instance.define(name, {
  8. gmtCreate: {
  9. type: Sequelize.DATE,
  10. allowNull: true,
  11. field: 'gmt_create',
  12. },
  13. gmtModified: {
  14. type: Sequelize.DATE,
  15. allowNull: true,
  16. field: 'gmt_modified',
  17. }
  18. }, {
  19. timestamps: true,
  20. createdAt: 'gmt_create',
  21. updatedAt: 'gmt_modified',
  22. freezeTableName: true,
  23. tableName: 'xxxx'
  24. });
  25. return UiKeyTraceModel;
  26. }
  27. providerWrapper([
  28. {
  29. id: 'keyTraceModel',
  30. provider: factory
  31. }
  32. ]);

通过 providerWrapper 我们将一个原本的函数写法进行了包裹,和现有的依赖注入体系可以融合到一起,让容器能够统一管理。