如果一個對象A需要依賴對象B,那么對象A不需要明確實例化對象B,B會由外部機制注入進來。

<1>提供器聲明在模塊中
1.新建一個項目
ng new projectname
2.新建組件product1
ng g component product1
3.生成服務(wù)service
ng g service shared/product
//生成一個名叫product的服務(wù),放在了shared文件夾下
ps:
若報錯
too many symbolic links encountered
解決辦法:
刪除node_modules文件夾, 重新npm install
4.目錄結(jié)構(gòu)

5.編寫服務(wù)product.service.ts
import { Injectable } from '@angular/core';
/*
@Injectable裝飾器作用:
讓ProductService能夠?qū)e的服務(wù)注入到它的構(gòu)造函數(shù)constructor中
*/
@Injectable()
export class ProductService {
constructor() { }
//3.聲明getProduct()方法,返回Product對象
getProduct():Product {
//4.返回--這里直接new了一個對象
return new Product(1,"iphone7",8799,"手機");
}
}
// 1.定義一個商品信息類product
export class Product {
// 2.構(gòu)造函數(shù):定義里面的字段
constructor(
public id:number,
public title:string,
public price:number,
public desc:string
){}
}
6.修改模塊聲明--app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';
// 2.引入
import { ProductService } from './shared/product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component
],
imports: [
BrowserModule
],
//1.提供器--添加聲明
providers: [ProductService],
bootstrap: [AppComponent]
})
export class AppModule { }
7.改寫組件--product1.component.ts
import { Component, OnInit } from '@angular/core';
//4.引入Product,ProductService
import { Product,ProductService } from '../shared/product.service';
@Component({
selector: 'app-product1',
templateUrl: './product1.component.html',
styleUrls: ['./product1.component.css']
})
export class Product1Component implements OnInit {
//1.聲明Product類型的組件用來接收從服務(wù)中獲取到的數(shù)據(jù)
product: Product;
//2.注入器:構(gòu)造函數(shù)中通過依賴注入,聲明token是ProductService的一個服務(wù)
constructor(private productService:ProductService) { }
ngOnInit() {
//3.使用服務(wù)的getProduct()方法獲取數(shù)據(jù),賦給本地product變量
this.product = this.productService.getProduct();
}
}
8.修改組件的模版,用于展示信息--product1.component.html
<div>
<h4>商品詳情</h4>
<h4>id:{{product.id}}</h4>
<h4>名稱:{{product.title}}</h4>
<h4>價格:{{product.price}}</h4>
<h4>描述:{{product.desc}}</h4>
</div>
9.修改主組件模版,來顯示商品組件--app.component.html
<div>
<div>依賴注入例子</div>
<div>
<app-product1></app-product1>
</div>
</div>
<2>提供器聲明在組件中
上面的例子中,提供器是聲明在app.module.ts中的,提供器也可以聲明在組件中,下面是例子。
1.聲明第二個組件
ng g component product2
2.生成另一個服務(wù)
ng g service shared/anotherProduct
3.編寫anotherProduct服務(wù)--another-product.service.ts
import { Injectable } from '@angular/core';
//4.import ProductService,Product
import { ProductService,Product } from './product.service';
@Injectable()
//1.實現(xiàn)ProductService,讓AnotherProductService和ProductService有相同方法
export class AnotherProductService implements ProductService{
//2.實現(xiàn)ProductService中的getProduct()方法
getProduct():Product {
//3.同樣返回一個商品對象
return new Product(2,"huawei",3000,"華為");
}
constructor() { }
}
4.修改模塊聲明--app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';
import { ProductService } from './shared/product.service';
//2.引入
import { Product2Component } from './product2/product2.component';
@NgModule({
declarations: [
AppComponent,
Product1Component,
//1.引入
Product2Component
],
imports: [
BrowserModule
],
providers: [ProductService],
bootstrap: [AppComponent]
})
export class AppModule { }
5.編寫product2.component.ts
構(gòu)造器部分與product1.component.ts相同,唯一不同的是多聲明了一個providers
import { Component, OnInit } from '@angular/core';
import { Product,ProductService } from '../shared/product.service';
//4.引入AnotherProductService
import { AnotherProductService } from '../shared/another-product.service';
@Component({
selector: 'app-product2',
templateUrl: './product2.component.html',
styleUrls: ['./product2.component.css'],
//2.在組件中聲明providers
providers:[{
//3.不同的是使用的是AnotherProductService服務(wù)
provide:ProductService,useClass:AnotherProductService
}]
})
export class Product2Component implements OnInit {
//1.與produce1相同
product: Product;
constructor(private productService:ProductService) { }
ngOnInit() {
this.product = this.productService.getProduct();
}
}
6.編寫product2.component.html
與product1模版相同
<div>
<h4>商品詳情</h4>
<h4>id:{{product.id}}</h4>
<h4>名稱:{{product.title}}</h4>
<h4>價格:{{product.price}}</h4>
<h4>描述:{{product.desc}}</h4>
</div>
7.編寫app.component.html
<div>
<div>依賴注入例子</div>
<div>
<app-product1></app-product1>
<!-- 引入product2組件 -->
<app-product2></app-product2>
</div>
</div>
提供器作用規(guī)則
1.當(dāng)提供其聲明在模塊中時(app.module.ts),是對所有模塊可見的,所有模塊都可以注入
2.當(dāng)一個提供器聲明在組件中時,例如(product2.component.ts),這個提供器只對聲明它的組件及
其子組件可見
3.當(dāng)聲明在組件中的提供器和模塊中的提供器具有相同的token時,組件中的提供器,
例如(product2.component.ts)會覆蓋聲明在模塊中的提供器
4.一般情況下應(yīng)優(yōu)先將提供器聲明在模塊中(app.module.ts),除非某提供器必須對其他組件不可見時
才聲明在組件中
<3>服務(wù)之間如何互相注入
1.生成新的服務(wù)logger
ng g service shared/logger
2.編寫logger.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
constructor() { }
//1.方法log(),接收一個string類型的message
log(message:string) {
//2.打印
console.log(message);
}
}
3.編寫product.service.ts--在其構(gòu)造函數(shù)中注入logger服務(wù)
import { Injectable } from '@angular/core';
//2.引入
import { LoggerService } from './logger.service';
@Injectable()
export class ProductService {
//1.注入logger服務(wù)
constructor(private logger:LoggerService) { }
getProduct():Product {
//3.調(diào)用logger中的log方法
this.logger.log("log message");
return new Product(1,"iphone7",8799,"手機");
}
}
export class Product {
constructor(
public id:number,
public title:string,
public price:number,
public desc:string
){}
}
4.app.module.ts模塊中聲明logger提供器--讓logger能被別的服務(wù)注入
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';
import { ProductService } from './shared/product.service';
import { Product2Component } from './product2/product2.component';
// 2.引入
import { LoggerService } from './shared/logger.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
//1.提供器--添加聲明LoggerService
providers: [ProductService,LoggerService],
bootstrap: [AppComponent]
})
export class AppModule { }
5.結(jié)果--控制臺中

PS:
1.只有聲明了@Injectable()裝飾器的服務(wù)才能注入其他服務(wù),建議所有服務(wù)都聲明@Injectable()裝飾器。
2.組件能夠注入服務(wù)的原因是聲明了@Component裝飾器,而這個裝飾器是@Injectable()裝飾器的子類
<4>使用工廠提供器和值聲明提供器
1.修改product2.component.ts
刪除以上例子中product2組件中的providers聲明,使product1和product2共用模塊中app.module.ts聲明的service
刪除
providers:[{
//3.不同的是使用的是AnotherProductService服務(wù)
provide:ProductService,useClass:AnotherProductService
}]
2.修改app.module.ts的提供器聲明
把providers: [ProductService,LoggerService],
bootstrap: [AppComponent]
改成
providers: [{
provide:ProductService,
//工廠提供器
useFactory:() => {
let logger = new LoggerService();
//設(shè)置隨機數(shù)來判斷使用哪個服務(wù)
let flag = Math.random() > 0.5;
if(flag){
return new ProductService(logger);
}else{
return new AnotherProductService(logger);
}
}
},LoggerService],
bootstrap: [AppComponent]
})
3.修改another-product.service.ts
import { Injectable } from '@angular/core';
import { ProductService,Product } from './product.service';
import { LoggerService } from './logger.service';
@Injectable()
export class AnotherProductService implements ProductService{
getProduct():Product {
return new Product(2,"huawei",3000,"華為");
}
//因為實現(xiàn)了ProductService,所以與ProductService的構(gòu)造函數(shù)一樣
constructor(public logger:LoggerService) { }
}
4.修改product.service.ts
把logger改為public
constructor(public logger:LoggerService) { }
5.結(jié)果
根據(jù)隨機數(shù)的不同,實現(xiàn)的服務(wù)不一樣


PS:
在這個例子中produce1和product2組件使用的服務(wù)永遠是相同的,因為工廠方法對象是一個單例對象,它只會在創(chuàng)建第一個需要注入的對象時被調(diào)用一次,然后在整個應(yīng)用中,所有被注入的ProductService的實例,都是同一個對象
問題1
在工廠方法中手工實現(xiàn)了一個LoggerService,意味著這個工廠方法與LoggerService類是緊密耦合在一起的
let logger = new LoggerService();
解決
在product的工廠方法useFactory中去使用LoggerService提供器,修改代碼:
providers: [{
provide:ProductService,
//2.把logger作為參數(shù)傳進去,
useFactory:(logger:LoggerService) => {
let flag = Math.random() > 0.5;
if(flag){
return new ProductService(logger);
}else{
return new AnotherProductService(logger);
}
},
//1.聲明第三個參數(shù)deps,用來聲明工廠方法所依賴的參數(shù)
//3.在deps里添加LoggerService,表明需要依賴LoggerService
deps:[LoggerService]
},LoggerService],
問題2
當(dāng)前實例化哪個對象使用flag隨機數(shù)來判斷的,在真實的項目中可能會依賴一個變量來判斷,這個變量可能在不同環(huán)境和變量是不一樣的
解決
變量也能像服務(wù)一樣進行依賴注入
providers: [{
provide:ProductService,
// 2.加上第二個參數(shù),這個參數(shù)自定義命名,對應(yīng)的是 deps中的第二個參數(shù)
useFactory:(logger:LoggerService,IsT) => {
if(IsT){
return new ProductService(logger);
}else{
return new AnotherProductService(logger);
}
},
//3.添加提供器的token:Is_True
deps:[LoggerService,"Is_True"]
},LoggerService,
// 1.第三個提供器,聲明了一個token是一個字符串,注入的就是明確的值flase
{
provide:"Is_True",useValue:false
}],
除了用具體值來定義一個提供器,也可以用一個對象來定義
providers: [{
provide:ProductService,
useFactory:(logger:LoggerService,IsT) => {
// 2.取對象中的isDev的值
if(IsT.isDev){
return new ProductService(logger);
}else{
return new AnotherProductService(logger);
}
},
deps:[LoggerService,"Is_True"]
},LoggerService,
{
//1.把Is_True改為對象,修改useValue
provide:"Is_True",useValue:{isDev:true}
}],