推荐答案
在 TypeScript 中,可以通过装饰器和反射元数据(Reflect Metadata)来实现一个简单的控制反转(IoC)容器。以下是一个基本的实现步骤:
- 定义装饰器:使用装饰器来标记需要注入的类或属性。
- 创建容器:使用一个容器来管理依赖关系。
- 注册依赖:将类注册到容器中,并指定它们的依赖关系。
- 解析依赖:通过容器解析依赖并创建实例。
以下是一个简单的实现示例:
// www.javascriptcn.com code example
import 'reflect-metadata';
const INJECTION_TOKEN_METADATA_KEY = 'injectionTokens';
class IoCContainer {
private instances = new Map<string, any>();
register<T>(token: string, target: new (...args: any[]) => T): void {
const injectionTokens = Reflect.getMetadata(INJECTION_TOKEN_METADATA_KEY, target) || [];
const dependencies = injectionTokens.map((token: string) => this.resolve(token));
this.instances.set(token, new target(...dependencies));
}
resolve<T>(token: string): T {
if (!this.instances.has(token)) {
throw new Error(`No provider found for ${token}`);
}
return this.instances.get(token);
}
}
function Injectable() {
return function (target: any) {
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, [], target);
};
}
function Inject(token: string) {
return function (target: any, key: string | symbol, index: number) {
const injectionTokens = Reflect.getMetadata(INJECTION_TOKEN_METADATA_KEY, target) || [];
injectionTokens[index] = token;
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, injectionTokens, target);
};
}
// 使用示例
@Injectable()
class ServiceA {
doSomething() {
console.log('ServiceA is doing something');
}
}
@Injectable()
class ServiceB {
constructor(@Inject('ServiceA') private serviceA: ServiceA) {}
doSomething() {
this.serviceA.doSomething();
console.log('ServiceB is doing something');
}
}
const container = new IoCContainer();
container.register('ServiceA', ServiceA);
container.register('ServiceB', ServiceB);
const serviceB = container.resolve<ServiceB>('ServiceB');
serviceB.doSomething();本题详细解读
1. 装饰器的作用
@Injectable():标记一个类为可注入的类,表示它可以被 IoC 容器管理。@Inject(token: string):用于标记构造函数参数,指定该参数需要注入的依赖。
2. IoC 容器的实现
IoCContainer:负责管理依赖关系和实例化对象。register:将类注册到容器中,并解析其依赖关系。resolve:从容器中获取已注册的实例。
3. 反射元数据的使用
Reflect.defineMetadata和Reflect.getMetadata:用于存储和获取类的元数据,特别是依赖注入的标记。
4. 依赖注入的过程
- 当注册一个类时,容器会解析其构造函数参数,并根据
@Inject装饰器指定的 token 查找对应的依赖。 - 容器会递归解析所有依赖,并最终创建类的实例。
5. 使用示例
ServiceA和ServiceB是两个可注入的类,ServiceB依赖于ServiceA。- 通过
IoCContainer注册和解析依赖,最终可以获取ServiceB的实例并调用其方法。
通过这种方式,TypeScript 中的装饰器和反射元数据可以有效地实现一个简单的 IoC 容器,从而实现控制反转。