Container 文档
Container#parent
public parent?: Container;parent 是公开可访问的实例属性,用于指向父级容器,从而可以形成树状容器结构。
container.get 方法在执行过程中会默认在当前容器寻找指定 token,如果找不到该 token,则会默认向父级容器中寻找。
一直重复这个过程,直到父级容器不存在为止。
Container#get
function get<T>(
token: CommonToken<T>,
options: Options<T> & { optional: true }
): T | void;
function get<T>(
token: CommonToken<T>,
options?: Options<T> & { optional?: false }
): T;
function get<T>(token: CommonToken<T>, options?: Options<T>): T | void;get 方法是 Container 最核心的方法,用于获取指定 token 对应的服务对象。当然前提是需要 Container 已经提前绑定了对应的 token。
options 中可以指定如下参数:
options.self; // 用于控制只在当前container中查询token
options.skipSelf; // 用于控制跳过当前container,从父级container中开始查询token
options.optional; // 当没有找到指定token时,默认时抛出异常,如果指定了options.optional=true,那么返回undefinedcontainer.get 方法可以自动根据 token 的类型自动推导出类型 T,所以不需要手动指定类型 T。
需要注意如果指定了 options.optional=true,则 get 方法有可能返回 undefined,所以需要自行在运行时做判空处理。
get 方法在实例化过程中会触发如下生命周期方法,顺序如下:
- Binding#onActivationHandler
- Container#onActivationHandler
- Class#PostConstruct
本库将 postConstruct 放在最后,是因为 postConstruct 执行时需要访问注入的属性,而属性注入发生在 activation 之后。更多详情参考 生命周期说明。
对比 inversify 中的生命周期方法,顺序如下:
- Class#PostConstruct
- Binding#onActivationHandler
- Container#onActivationHandler
Container#getAsync
function getAsync<T>(
token: CommonToken<T>,
options: Options<T> & { optional: true }
): Promise<T | void>;
function getAsync<T>(
token: CommonToken<T>,
options?: Options<T> & { optional?: false }
): Promise<T>;
function getAsync<T>(token: CommonToken<T>, options?: Options<T>): Promise<T | void>;getAsync 是 get 的异步版本,等待服务的 @PostConstruct 方法执行完成后再返回实例。
get 方法是同步的,如果服务的 @PostConstruct 是异步方法,get 返回时 @PostConstruct 可能还未执行完毕。getAsync 解决了这个问题:它内部调用 get 获取实例后,如果 binding.postConstructResult 是 Promise,会 await 该 Promise,确保调用者拿到的实例已完全初始化。
// 使用 get:同步返回,但异步 PostConstruct 可能还没完成
const db = container.get(DatabaseService);
db.query(); // 可能失败,因为数据库连接还没建立
// 使用 getAsync:等待 PostConstruct 完成后再返回
const db = await container.getAsync(DatabaseService);
db.query(); // 安全,数据库连接已建立getAsync 支持与 get 相同的 options 参数(self、skipSelf、optional)。
如果 @PostConstruct 执行失败(抛出异常或返回 rejected Promise),getAsync 返回的 Promise 也会被 reject:
try {
const service = await container.getAsync(UnstableService);
} catch (error) {
console.log('服务初始化失败:', error.message);
}Container#bind
function bind<T>(token: CommonToken<T>): Binding<T>;绑定指定的 token,并返回 Binding 对象。可以通过 Binding 对象关联对应的服务。这样就可以把 token 和服务关联起来了。
Container#unbind
function unbind<T>(token: CommonToken<T>): void;解绑指定的 token,会触发相应的生命周期方法。执行顺序如下:
- Container#onDeactivationHandler
- Binding#onDeactivationHandler
- Class#PreDestroy
这里的顺序是和inversify 的执行顺序保持一致的。
Container#unbindAll
function unbindAll(): void;会解绑容器内所有的 token,并触发对应的生命周期方法。
Container#isCurrentBound
function isCurrentBound<T>(token: CommonToken<T>): boolean;判断当前容器是否绑定了指定的 token。
Container#isBound
function isBound<T>(token: CommonToken<T>): boolean;判断当前容器以及所有父级容器是否绑定了指定的 token。
Container#children
public children?: Set<Container>;children 是可选的公开属性,类型为 Set<Container> | undefined,用于记录当前容器的所有直接子容器。
- 初始状态下
children为undefined,只有在第一次调用createChild()后才会被初始化为Set - 每次调用
createChild()时,新创建的子容器会自动被添加到父容器的children集合中 - 当调用子容器的
destroy()时,子容器会自动从父容器的children集合中移除
Container#createChild
function createChild(): Container;快速创建一个子容器。过程如下:
const childContainer = new Container();
childContainer.parent = this;
if (!this.children) {
this.children = new Set();
}
this.children.add(childContainer);
return childContainer;Container#destroy
function destroy(): void;相对于 createChild 方法是创建一个新的容器,destroy 方法则负责销毁自身。 在 unbindAll 方法的基础上清除了所有自身的状态。
注意 inversify 中并没有提供这个方法。
destroy 会递归销毁所有子容器(遍历 children 集合,对每个子容器调用 destroy()),销毁完成后将自身从父容器的 children 集合中移除。
const parent = new Container();
const child1 = parent.createChild();
const child2 = parent.createChild();
// 递归销毁 parent 及其所有子容器(child1、child2 也会被一并销毁)
parent.destroy();
// 也可以单独销毁某个子容器,child1 会自动从 parent.children 中移除
child2.destroy();Container#onActivation
function onActivation<T>(handler: ActivationHandler<T>): void;
// ActivationHandler 完整签名:
type ActivationHandler<T> = (ctx: Context, input: T, token?: CommonToken<T>) => T;注册一个 Activation 函数,会在 get 方法首次执行过程中被执行。
注意只能注册一个 Activation 函数,重复注册,只会覆盖前一个函数。
这个 Activation 函数是当前 Container 的,当前 Container 的所有 token 都会使用这个 Activation 函数。
可选的 token 参数可用于区分不同 token,从而实现差异化逻辑:
container.onActivation((ctx, input, token) => {
if (token === LoggerService) {
console.log('LoggerService activated');
}
return input;
});Container#onDeactivation
function onDeactivation<T>(handler: DeactivationHandler<T>): void;
// DeactivationHandler 完整签名:
type DeactivationHandler<T> = (input: T, token?: CommonToken<T>) => void;注册一个 Deactivation 函数,会在 unbind 方法执行过程中被执行。
注意只能注册一个 Deactivation 函数,重复注册,只会覆盖前一个函数。
这个 Deactivation 函数是当前 Container 的,当前 Container 的所有 token 都会使用这个 Deactivation 函数。
可选的 token 参数可用于区分不同 token,从而实现差异化逻辑:
container.onDeactivation((input, token) => {
if (token === LoggerService) {
console.log('LoggerService deactivated');
}
});Container.getContainerOf
static getContainerOf(instance: object): Container | undefined;静态方法,用于获取某个服务实例所属的容器。
- 只有通过
to()或toSelf()绑定的 class 服务实例才会被记录; toConstantValue和toDynamicValue的值不会被记录- 主要用于
@LazyInject内部实现:当@LazyInject没有显式指定 container 时,通过此方法从实例反查所属容器
const container = new Container();
container.bind(MyService).toSelf();
const instance = container.get(MyService);
Container.getContainerOf(instance); // 返回 containerContainer._instanceContainerMap
内部使用 WeakMap<object, Container> 存储实例与容器的映射关系:
static _instanceContainerMap = new WeakMap<object, Container>();- 只有
Instance类型(to()/toSelf())的绑定在实例化时才会注册到此映射 toConstantValue和toDynamicValue不会注册,原因是同一个对象可能被绑定到多个容器,WeakMap只能保留最后一次映射,会导致@LazyInject从错误的容器解析依赖- 由于
Instance类型每次都通过new ClassName()创建新实例,不存在同一实例被多个容器注册的覆盖风险