IOC的一个很重要的实现就是依赖注入,将用户对类实例化的操作权,反转给容器去做,由容器来处理类与类之间的依赖关系,从而可以降低模块与模块之间的耦合关系。
下面是手动实现的依赖注入原理:
// es7的提案,ts1.5+版本已经支持,可以通过他给类或者类的原型属性上添加元数据
import 'reflect-metadata';
const INJECTED = '__inject__';
type Constructor<T = any> = new (...args: any[]) => T;
// 定义一个装饰器,他可以在类的构造函数上定义元数据,定义的元数据是构造函数的所有参数
const Injectable = (): ClassDecorator => (constructor) => {
Reflect.defineMetadata(
INJECTED,
Reflect.getMetadata('design:paramtypes', constructor), // 这里还支持两外两种内置元数据定义,一个是design:type获取属性类型,一个是design:returntype获取返回值类型
constructor,
);
};
@Injectable()
class AService {
a = 1;
}
@Injectable()
class BService {
b = 2;
constructor(private aService: AService) {}
}
@Injectable()
class TestService {
constructor(
private obj,
private aService: AService,
private bService: BService,
) {}
}
const getInstance = <T>(target: Constructor<T>): T => {
// 获取所有注入的服务
const providers = Reflect.getMetadata(INJECTED, target);
const args =
providers?.map((provider: Constructor) => {
console.log(provider);
return getInstance(provider); // 递归实例化所有依赖
}) ?? [];
return new target(...args);
};
console.log(getInstance(TestService));
// 打印结果:
/*
* [Function: Object]
* [class AService]
* [class BService]
* [class AService]
* TestService {
* obj: {},
* aService: AService { a: 1 },
* bService: BService { aService: AService { a: 1 }, b: 2 }
* }
*/
特点:通过容器完成对象的装配,注入到需要的对象(被动获取)
因此依赖注入又被称之为好莱坞原则:”Don't call us, we'll call you”,有事儿不要找我们,如果有需要我们会找你!