实现思路
- 定义装饰器:使用 TypeScript 装饰器来标记需要注入的服务类和注入点。
- 创建服务容器:用于存储和管理服务实例。
- 解析依赖关系:在实例化组件或服务时,根据装饰器标记从服务容器中获取依赖的实例并注入。
关键代码示例
- 定义装饰器
// 用于标记服务类的装饰器
function Injectable() {
return function (target: Function) {
Reflect.defineMetadata('isInjectable', true, target);
};
}
// 用于标记注入点的装饰器
function Inject() {
return function (target: any, propertyKey: string) {
const type = Reflect.getMetadata('design:type', target, propertyKey);
Reflect.defineMetadata('injectProperty', type, target, propertyKey);
};
}
- 创建服务容器
class ServiceContainer {
private services: Map<Function, any> = new Map();
register<T>(service: new () => T): void {
if (Reflect.getMetadata('isInjectable', service)) {
this.services.set(service, new service());
} else {
throw new Error('The service is not injectable');
}
}
get<T>(service: new () => T): T {
return this.services.get(service) as T;
}
}
- 使用示例
// 定义一个可注入的服务
@Injectable()
class LoggerService {
log(message: string) {
console.log(`[LOG] ${message}`);
}
}
// 定义一个依赖 LoggerService 的组件
class AppComponent {
@Inject()
private logger: LoggerService;
constructor() {
// 这里会在后续依赖注入时填充 logger
}
doSomething() {
this.logger.log('Doing something...');
}
}
// 使用服务容器
const container = new ServiceContainer();
container.register(LoggerService);
const appComponent = new AppComponent();
// 手动解析依赖并注入
const injectProperties = Object.getOwnPropertyNames(Object.getPrototypeOf(appComponent)).filter(key =>
Reflect.getMetadata('injectProperty', appComponent, key)
);
injectProperties.forEach(key => {
const type = Reflect.getMetadata('injectProperty', appComponent, key);
appComponent[key] = container.get(type);
});
appComponent.doSomething();