依赖注入是控制反转的一种实现方式。这种模式就是Java Web开发中最流行框架Spring的核心概念。
为什么需要使用依赖注入
在原来最普通的开发中,如果我们有一个方法createCar(new Wheel(), new Engine(), new CarBody())。那么我们得需要分别实例化,轮胎,引擎,车身给这个方法。这种形式叫做注入。 很有可能出现的一种情况是车身这个对象中还包括了:座椅,方向盘,仪表盘,手刹,等等一系列的东西,我们就需要使用大量实例化代码。这个时候依赖注入出现了,可以帮我们解决上述的问题。
不仅如此,而且会使得代码呈现一种松耦合的状态,并且使得可重用性更高。
使用依赖注入
-
如何注册注入: 使用提供器(Providers),在app.module.ts中有一个属性叫做providers。
providers: [ TestService ]
providers是一个数组,那么他的每一项叫做一个provider。
上面所说的providers其实是一种省略的写法,完整的写法是这样的:
providers: [
{provider: TestService, useClass: TestService}
]
- 对象中的provider属性,(这里的TestService )专业一点的说叫做为Token,指定的是这个依赖注入的类型。
- 每当有地方要使用这个Service的时候,就会把useClass属性后面指定的实例化一下,然后进行返回。如果使用过spring的话,一定不会陌生。简单的说,就是给出了一个接口(provider 属性指定的),然后就可以附上不同的实现类(useClass属性的)
- 除了上述情况,还有一种情况就是我们需要动态的根据某一个值指定到底使用哪个对象,这个时候我们就需要使用useFactory属性
因为factory是单例的,所以返回的结果在当前应该是同一个,如果同时取得两个注入器的话。
providers: [{provide: TestService, useFactory: (logger: LoggerService) => {
//这里暂时使用一个随机数代替,决定了到底使用哪一个对象
if (Math.random() > 0.5) {
//这里一定要记的return 否则注入会成为undefined.
return new ServiceService(logger);
} else {
return new AnotherServiceService(logger);
}},deps: [LoggerService]]
deps属性指定了我们使用工厂注入的服务中,还需要注入哪些服务。
-
而且不光可以注册服务,还可以对值进行注册,注册的方式如下:
{ provide: 'IS_DEV_ENVIRONMENT', useValue: false }
-
提供器作用域
- 当提供器在模块中时,服务是服务于全部component的,相当于是全局的 。
- 当提供器在组件中时,服务是服务于组件以及自组件的。
- 当相同token的提供器既存在于模块也存在在组件中时,组件中的优先级更高。
- 优先将服务声明在组件中。尽量缩小代码的作用域是程序员必备准则。
Tips: @Injectable 注解表明的是当前的这个服务可不可以再注入别的服务。建议不管是否需要,都要加上这个注解,@Component是这个注解的子类,所以我们可以直接在组件中进行注入
- 如何使用注入: 使用注入器
在具体的component文件中,我们通过构造函数指定,例如说像下方一样,指定具体需要的Token。然后通过指定的Token去返回指定对象的实例化
constructor(testService: TestService) {
}
- 注入器的加载顺序
注入器是通过提供器提供的服务进行注册的,这个众所周知。但是它是一个怎样的加载过程呢,这就应该好好说一说注入器的层级关系了。
app module为整个应用最根本的源头,所以说他就是最外的一层(应用级别注入器)。这个app module可能包含有别的Module,那些module会有自己的注入器,他们被称作(主组件注入器)。这些组件下方还会包含有子组件,他们被称作子组件注入器。整个加载过程类似JVM的双亲委托。我现在有一个子组件,注入了一个服务,首先会查找当前的提供器是否有包含,如果没有,继续向父组件找。然后就这么一层一层的找,直到到了最外层提供器,还是没有找到的话,则会报出异常。