装饰器
本节介绍用于提供类元数据的 Inversify 装饰器。
injectable
用于设置类元数据的装饰器,以便容器可以接收类发出的元数据。
强烈建议使用 @injectable 装饰器注释作为服务提供的每个类。但是,并非在每种情况下都是强制性的。
什么时候 injectable 是强制性的?
每当预期类发出的元数据时。
考虑以下示例代码:
import 'reflect-metadata/lite';
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 });
require("reflect-metadata");
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 });
require("reflect-metadata");
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;
multiInject
用于在构造函数参数或类属性与服务 ID 之间建立关系的装饰器。
解析类的实例时,目标构造函数参数或属性将以与启用 enforceBindingConstraints 标志的 container.getAll 行为相同的方式解析。
示例:装饰属性
@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).whenTargetNamed('melee');
container.bind<Weapon>('Weapon').to(Shuriken).whenTargetNamed('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;
}
export 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');
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)
.whenTargetTagged('weaponKind', 'melee');
container
.bind<Weapon>('Weapon')
.to(Shuriken)
.whenTargetTagged('weaponKind', 'ranged');
container.bind(Ninja).toSelf();
const ninja: Ninja = container.get(Ninja);
targetName
用于在设计时在构造函数参数或类属性名称之间建立关系的装饰器。
打包器可能会缩小代码,从而更改类属性名称。此装饰器跟踪原始名称。
此属性保存在绑定约束中 target 请求的 name 属性中。
@injectable()
class Ninja implements Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject("Weapon") @targetName("katana") katana: Weapon,
@inject("Weapon") @targetName("shuriken") shuriken: Weapon
) {
this.katana = katana;
this.shuriken = shuriken;
}
}
container.bind<Weapon>("Weapon").to(Katana).when((request: interfaces.Request) => {
return request.target.name.equals("katana");
});
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();
const derived: Derived = container.resolve(Derived);
// Returns 'inherited-value'
const derivedProp: string = derived.prop;