装饰器
本节介绍用于提供类元数据的 Inversify 装饰器。
injectable
用于设置类元数据的装饰器,以便容器可以接收类发出的元数据。
强烈建议使用 @injectable 装饰器注释作为服务提供的每个类。但是,并非在每种情况下都是强制性的。
参数
injectable 装饰器接受一个可选的 scope 参数,允许你定义绑定实例的生命周期:
@injectable('Singleton') // or 'Transient' or 'Request'
class MyService {
// ...
}
有效的作用域值为:
'Singleton':容器将仅创建一个实例,并为所有请求返回该实例。'Transient':容器将为每个请求创建一个新实例。'Request':容器将为每个唯一请求创建一个实例(在具有请求作用域的 Web 应用程序中很有用)。
什么时候 injectable 是强制性的?
每当预期有类发出的元数据时。
考虑以下示例代码:
import { injectable } from 'inversify';
@injectable()
class B {
readonly foo: string = 'foo';
}
@injectable()
class A {
constructor(public readonly b: B) {}
}
启用 emitDecoratorMetadata 选项的 CommonJS 转译可能如下所示:
Object.defineProperty(exports, "__esModule", { value: true });
const inversify_1 = require("inversify");
let B = class B {
foo = 'foo';
};
B = __decorate([
(0, inversify_1.injectable)()
], B);
let A = class A {
b;
constructor(b) {
this.b = b;
}
};
A = __decorate([
(0, inversify_1.injectable)(),
__metadata("design:paramtypes", [B])
], A);
当且仅当目标类应用了任何类装饰器时,TypeScript 才会发出类元数据。如果我们从 A 中删除 @injectable,则转译后的代码看起来会有很大不同:
Object.defineProperty(exports, "__esModule", { value: true });
class B {
foo = 'foo';
}
class A {
b;
constructor(b) {
this.b = b;
}
}
这一次,即使启用了 emitDecoratorMetadata TypeScript 选项,也不会发出类元数据,从而导致执行时出现问题。
inject
用于在构造函数参数或类属性与服务 ID 之间建立关系的装饰器。
解析类的实例时,目标构造函数参数或属性将以与 container.get 相同的方式解析。
示例:装饰类构造函数参数
@injectable()
class Ninja {
constructor(
@inject(weaponServiceId)
public readonly weapon: Weapon,
) {}
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;
示例:装饰属性
@injectable()
class Ninja {
@inject(weaponServiceId)
public readonly weapon!: Weapon;
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;
injectFromBase
用于从基类注入依赖项的装饰器。子类注入优先于基类注入。
参数
injectFromBase 装饰器接受一个可选的 InjectFromBaseOptions 对象,具有以下属性:
extendConstructorArguments(boolean,默认为 true):如果为 true,将注入基类的构造函数参数。extendProperties(boolean,默认为 true):如果为 true,将注入基类的属性。lifecycle(object,可选):用于扩展基类生命周期方法的生命周期选项:extendPostConstructMethods(boolean,默认为 true):如果为 true,将扩展基类的 post-construct 方法。extendPreDestroyMethods(boolean,默认为 true):如果为 true,将扩展基类的 pre-destroy 方法。
示例:装饰基类构造函数参数
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}
@injectable()
@injectFromBase({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends BaseSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
示例:装饰基类属性
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}
@injectable()
@injectFromBase({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends BaseSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
injectFromHierarchy
用于从完整的类层级结构(所有祖先)注入依赖项的装饰器。注入是自上而下应用的(从最远的祖先到最近的基类)。
参数
injectFromHierarchy 装饰器接受一个可选的 InjectFromHierarchyOptions 对象,具有以下属性:
extendConstructorArguments(boolean,默认为 true):如果为 true,将注入所有祖先的构造函数参数。extendProperties(boolean,默认为 true):如果为 true,将注入所有祖先的属性。lifecycle(object,可选):用于扩展层级结构生命周期方法的生命周期选项:extendPostConstructMethods(boolean,默认为 true):如果为 true,将扩展所有祖先的 post-construct 方法。extendPreDestroyMethods(boolean,默认为 true):如果为 true,将扩展所有祖先的 pre-destroy 方法。
示例:装饰层级结构构造函数参数
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}
@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}
@injectable()
@injectFromHierarchy({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends IntermediateSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
示例:装饰层级结构属性
type Weapon = unknown;
@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}
@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}
@injectable()
@injectFromHierarchy({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends IntermediateSoldier {}
// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);
multiInject
用于在构造函数参数或类属性与服务 ID 之间建立关系的装饰器。
解析类的实例时,目标构造函数参数或属性将以与 container.getAll 相同的方式解析。
参数
multiInject 装饰器接受一个可选的 MultiInjectOptions 对象,具有以下属性:
chained(boolean):启用跨容器层级结构的链式解析的选项。有关更多信息,请参阅 文档。
示例:装饰属性
@injectable()
class Ninja {
@multiInject(weaponServiceId)
public readonly weapon!: Weapon[];
}
const container: Container = new Container();
container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);
const ninja: Ninja = container.get(Ninja);
// Returns 10
const ninjaWeaponDamage: number | undefined = ninja.weapon[0]?.damage;
named
用于在构造函数参数或类属性与元数据名称之间建立关系的装饰器。
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @named('melee') katana: Weapon,
@inject('Weapon') @named('ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenNamed('melee');
container.bind<Weapon>('Weapon').to(Shuriken).whenNamed('ranged');
container.bind(Ninja).toSelf();
const ninja: Ninja = container.get(Ninja);
optional
用于确定目标构造函数参数或属性是可选的装饰器,因此,如果未找到关联服务 ID 的绑定,则不应解析它。
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon | undefined;
constructor(
@inject('Katana') katana: Weapon,
@inject('Shuriken') @optional() shuriken: Weapon | undefined,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Katana').to(Katana);
container.bind(Ninja).toSelf();
// Returns a "Ninja" instance with a "Katana" katana property and undefined shuriken property.
const ninja: Ninja = container.get(Ninja);
postConstruct
用于为目标类建立激活处理程序的装饰器。有关更多信息,请参阅 文档。
interface Weapon {
damage: number;
}
export class Katana implements Weapon {
#damage: number = 10;
public get damage(): number {
return this.#damage;
}
@postConstruct()
public improve(): void {
this.#damage += 2;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
// Katana.damage is 12
const katana: Weapon = container.get<Weapon>('Weapon');
preDestroy
用于为目标类建立停用处理程序的装饰器。有关更多信息,请参阅 文档。
interface Weapon {
damage: number;
}
class Katana implements Weapon {
readonly #damage: number = 10;
public get damage(): number {
return this.#damage;
}
@preDestroy()
public onDeactivation(): void {
console.log(`Deactivating weapon with damage ${this.damage.toString()}`);
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).inSingletonScope();
container.get('Weapon');
await container.unbind('Weapon');
tagged
用于在构造函数参数或类属性与元数据标签之间建立关系的装饰器。
@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @tagged('weaponKind', 'melee') katana: Weapon,
@inject('Weapon') @tagged('weaponKind', 'ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenTagged('weaponKind', 'melee');
container
.bind<Weapon>('Weapon')
.to(Shuriken)
.whenTagged('weaponKind', 'ranged');
container.bind(Ninja).toSelf();
const ninja: Ninja = container.get(Ninja);
unmanaged
用于确定 Inversify 不应注入目标构造函数参数或属性的装饰器。
import { Container, injectable, unmanaged } from 'inversify';
@injectable()
class Base {
public prop: string;
constructor(@unmanaged() arg: string) {
this.prop = arg;
}
}
@injectable()
class Derived extends Base {
constructor() {
super('inherited-value');
}
}
const container: Container = new Container();
container.bind(Derived).toSelf();
const derived: Derived = container.get(Derived);
// Returns 'inherited-value'
const derivedProp: string = derived.prop;