如何使用Inversify初始化含嵌套依赖的类构造函数
InversifyJS 启动阶段初始化订阅器的正确实现方案
现有代码存在5个核心问题会导致初始化失败:
- 需要被容器管理的类未添加
@injectable()装饰器,Inversify无法识别构造函数的注入参数 - 依赖绑定顺序错误,未遵循「底层依赖先绑定、上层服务后绑定」的原则
AsyncContainerModule中await AppSubscribers/await Locators属于无效写法,类本身不是Promise实例,await不会触发任何初始化逻辑- 构造函数内属性名拼写错误,定义的注入属性为
locators,调用时写成了this._locators,会触发undefined报错 - Inversify默认是懒加载模式,仅完成绑定不会主动实例化类,构造函数内的订阅注册逻辑永远不会执行
第一步:修正类定义的装饰器和笔误
所有需要注入依赖、或被注入到其他类的服务,都必须添加@injectable()装饰器,同时修正属性名、类型导入的笔误:
// app-subscribers.ts(原locator.ts) import { inject, injectable } from 'inversify'; import TYPES from '@shared/di/types/types'; // 修正原代码导入类型和注入类型不匹配的笔误 import Locators from '../.Locators'; import { AfterTypeCreated } from './AfterTypeCreated'; import { logger } from '@shared/core/logger/logger'; @injectable() // 必须添加该装饰器,否则Inversify无法处理构造函数注入 export class AppSubscribers { constructor( @inject(TYPES.Locators) private locators: Locators ) { logger.info('SUBSCRIBER STARTED'); // 修正原代码this._locators的笔误 new AfterTypeCreated(this.locators); } }
注意:
Locators、Repository类如果存在构造函数注入,也需要添加@injectable()装饰器,注入参数用@inject标注对应标识即可。
第二步:修正容器绑定逻辑
删除无效的await类写法,按照依赖层级从下到上绑定,全局单例服务明确指定单例作用域,避免重复实例化:
// config.ts export const bindings = new AsyncContainerModule(async (bind) => { // 先初始化最底层依赖:数据库连接 await getDbConnection(); // 按依赖层级从低到高绑定,推荐全局服务使用单例作用域 bind<IRepository>(TYPES.IRepository).to(Repository).inSingletonScope(); bind<Locators>(TYPES.Locators).to(Locators).inSingletonScope(); bind<AppSubscribers>(TYPES.AppSubscribers).to(AppSubscribers).inSingletonScope(); bind<RouterClass>(TYPES.RouterClass).to(RouterClass).inSingletonScope(); });
第三步:应用启动时主动触发实例化
容器加载完成后,主动获取AppSubscribers实例,触发构造函数执行完成订阅注册,这一步是启动初始化的核心:
// 应用启动入口文件(app.ts / index.ts) import { Container } from 'inversify'; import { bindings } from './config'; import TYPES from '@shared/di/types/types'; import type { AppSubscribers } from './path-to-app-subscribers'; async function bootstrap() { const container = new Container(); // 加载异步绑定模块 await container.loadAsync(bindings); // 主动resolve实例,触发构造函数内的订阅注册逻辑 // 单例作用域下后续重复get会返回同一实例,不会重复初始化 container.get<AppSubscribers>(TYPES.AppSubscribers); // 后续执行路由初始化、服务启动等逻辑 // const router = container.get<RouterClass>(TYPES.RouterClass); // router.listen(3000); } bootstrap();
额外注意事项
- 禁止在类静态块中写依赖注入相关的初始化逻辑,静态块执行时机早于容器绑定完成,无法拿到注入的依赖实例
- 如果存在多个订阅器,可以在
AppSubscribers的构造函数中注入所有订阅器实例,仅需在入口resolve一次AppSubscribers即可完成全量订阅注册 - 绑定作用域根据业务场景选择:全局唯一的服务(数据库连接、注册中心、订阅器)用
inSingletonScope(),请求级别的服务用inRequestScope(),临时实例用默认的瞬态作用域即可
内容的提问来源于stack exchange,提问作者Mr X
相关产品推荐
相关产品推荐

