设计思路
- 定义依赖注入装饰器:通过装饰器来标记需要注入依赖的方法,并指定依赖的类型或名称。
- 管理依赖容器:创建一个容器来存储所有的依赖实例,负责依赖的创建、生命周期管理和作用域控制。
- 解析依赖:在方法执行前,从依赖容器中获取所需的依赖实例,并注入到方法的参数列表中。
关键代码示例
- 定义依赖注入装饰器
function Injectable() {
return function (target: any) {
Reflect.defineMetadata('isInjectable', true, target);
};
}
function Inject(dependencyKey: string | symbol) {
return function (target: any, propertyKey: string, parameterIndex: number) {
let dependencies: { [key: string | symbol]: number[] } = Reflect.getOwnMetadata('dependencies', target, propertyKey) || {};
if (!dependencies[dependencyKey]) {
dependencies[dependencyKey] = [];
}
dependencies[dependencyKey].push(parameterIndex);
Reflect.defineMetadata('dependencies', dependencies, target, propertyKey);
};
}
- 依赖容器
class DependencyContainer {
private instances: { [key: string | symbol]: any } = {};
private factories: { [key: string | symbol]: () => any } = {};
register(key: string | symbol, factory: () => any) {
this.factories[key] = factory;
}
get(key: string | symbol): any {
if (!this.instances[key]) {
if (!this.factories[key]) {
throw new Error(`Dependency with key '${key}' not registered.`);
}
this.instances[key] = this.factories[key]();
}
return this.instances[key];
}
}
- 使用装饰器和依赖容器
@Injectable()
class Logger {
log(message: string) {
console.log(`[LOG] ${message}`);
}
}
@Injectable()
class DataService {
constructor(private logger: Logger) {}
getData() {
this.logger.log('Fetching data...');
return 'Some data';
}
}
const container = new DependencyContainer();
container.register('Logger', () => new Logger());
container.register('DataService', () => new DataService(container.get('Logger')));
function resolveDependencies(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const dependencies: { [key: string | symbol]: number[] } = Reflect.getOwnMetadata('dependencies', target, propertyKey) || {};
Object.keys(dependencies).forEach(key => {
const paramIndices = dependencies[key];
paramIndices.forEach(index => {
args[index] = container.get(key);
});
});
return originalMethod.apply(this, args);
};
return descriptor;
}
class App {
@resolveDependencies
start(dataService: DataService) {
const data = dataService.getData();
console.log('App started with data:', data);
}
}
const app = new App();
app.start();