---
title: 'Dagger 依賴注入'
disqus: kyleAlien
---
Dagger 依賴注入
===
## OverView of Content
如有引用參考請詳註出處,感謝 :smile:
在學習前先 Ioc 之前要先有 **^1^ Java 註解、^2^ Java反射 這兩個基礎**,並且要大致上了解到 6 大基礎的設計模式,尤其是**依賴倒置(Dependence Inversion Principle)**
[TOC]
## 何謂依賴
類與類之間的連接,是實體定義,連接固定的實做類方法
```java=
public class Dependency {
public static void main(String[] args) {
new Car().DriveInfo();
}
}
class Car {
private Gasoline92 g92;
Car() {
//"1. "
g92 = new Gasoline92();
}
void DriveInfo() {
System.out.println("This car use " + g92.getType());
}
}
class Gasoline92 {
String getType() {
return "92";
}
}
```
1. 車直接依賴 92 的汽油,但是如要要使用 95 的汽油就必須在該類內修改,這樣**不符合開閉原則**,若是需要拓展也相當不方便
**--實作--**
> 
### 依賴倒置
* 先提到 OOP 設計模式的六大原則之一,**依賴倒置,高層模組不該依賴低層模組,應該依賴抽象,並且 ==所謂的抽象,是一種相對關係==,並沒有說絕對的抽象**
```java=
//1. 抽象
interface Gasoline {
String getType();
}
class Car {
private Gasoline g;
Car() {
Gasoline = new Gasoline95(); // 2. 調用實體類
}
void DriveInfo() {
// 呼叫 Gasoline 抽象的相同方法
System.out.println("This car use " + g.getType()); // 不用更改方法
}
}
// 實作抽象類別
class Gasoline92 implements Gasoline{
@Override
public String getType() {
return "92";
}
}
class Gasoline95 implements Gasoline{
@Override
public String getType() {
return "95";
}
}
```
1. 依賴抽象接口,讓實作的類去實現這個接口
> 如何注入 (Inject) 之後說明
2. 當需求改變唯一需要改動到的就是呼叫實體類(這裡呼叫實體類仍是包含在依賴倒置的範圍內),而呼叫方法的位置則不須做任何更改
:::info
* 上層 & 下層模組?
^1^ 上層模塊:呼叫者
^2^ 下層模塊:被呼叫者
> 依賴倒置: 簡單來說就是要讓 Class 去依賴抽象,而不是依賴實做,這樣對於 **類的擴充來講更加的好**
- **抽象**:不須直接實現實際要操作的邏輯,而是定義它的抽象方法,它可以是行為、物品 ... 等等
- **實做**,則是實際實現邏輯 & 數據的地方,它是具體的結果
:::
### 控制反轉 IoC - 抽象化
* IoC 是 **Inversion of Control**,反轉的就是 ==**對於控制權的反轉**==,**變成依賴注入** DI (Dependency Injection)
> 透過注入的類,來決定實作細節
* 將原本的程式需要手動去獲取需要的類,**轉變為透過第三方獲取並使原來的程式被動接收,來達到解偶的效果**,Android 中最常是配合反射一起使用
> 
### 依賴注入 - 注入方式
* 會關心到要如何注入 **抽象** 接口,有三個方法
1. 建構函數 constructor
2. setting interface
3. 該類實做抽象接口
```java=
public class Dependency {
public static void main(String[] args) {
new Car(new Gasoline95()).DriveInfo();
}
}
interface Gasoline {
String getType();
}
// 注入 接口 InjectGas
interface InjectGas {
void useGas(Gasoline g);
}
class Car implements InjectGas {
private Gasoline g;
//"1. " 建構函數注入
Car(Gasoline g) {
this.g = g;
}
//"2. " 方法注入
void setGasline(Gasoline g) {
this.g = g;
}
//"3. " Gasoline#interface 注入
@Override
public void useGas(Gasoline g) {
this.g = g;
}
void DriveInfo() {
System.out.println("This car use " + g.getType());
}
}
class Gasoline92 implements Gasoline{
@Override
public String getType() {
return "92";
}
}
class Gasoline95 implements Gasoline{
@Override
public String getType() {
return "95";
}
}
```
**--實作--**
> 
## 標準 Java 依賴注入
**JSR-300** 是 Java 的依賴注入標準,(JSR-330,Dependency Injection for Java)
### javax.inject
* 由於 inject 是一**個單獨的 Jar 包,並不包含在 JDK 中**,先下載 [**javax.inject**](https://search.maven.org/artifact/javax.inject/javax.inject/1/jar) 的 Jar 包在手動添加到 JDK 中
* 它指定了獲取對象的一種方法,該方法與構造器、工廠以及服務定位這些傳統方法比起來有更好的 **可重用、測試、維護性**,==此方法的處理過程就是依賴注入==
> 下面會有範例
* 在查詢依賴的類別時,這個行為稱為 ==解析依賴==,找不到依賴的例項稱為 **不能滿足**
### @Inject & @Provider<T\>
* 註解 @Inject 標示 可被注入的 ^1.^ 構造器(construct)、^2.^ 方法(method)、^3.^ 字段(Field),並且可被其他修飾符修飾 (private、protected、public),**注入順序為 構造器 > 字段 > 方法**
```java=
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}
```
* 註解 @Provider 用來==提供類的實例== (泛型)
### @Qulifier & @Named
* 註解 @Qulifier 可以用來區分要使用哪一個實現來提供,它是專門用來註解註解的(註解的註解 :smile:)
```java=
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Qualifier {}
```
* 註解 @Named 單例是 Javax 內置的註釋,本身被 @Qulifier 所註釋
### @Scope & @Singleton
* 註解 @Scope 範疇,可以用來時局部、全局單例,**主要看使用者在哪裡建立**,若是重新建立則會失去單例作用(變成了局部單例),**==Scope 本身不具有單例的功能==**,它是專門用來註解註解的
```java=
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}
```
* 註解 @Singleton 單例是 Javax 內置的註釋,本身被 @Scope 所註釋
## Dagger2
**Dagger2 是基於 JSR-300 標準 (Java 依賴注入) 的依賴注入框架**,在編譯期間自動生成程式,**負責依賴對象的創建**,Dagger2 是 Dagger1 的分支,之後由 Google 接手進行開發
* 要使用必須在 Gradle 添加依賴
```groovy=
dependencies {
...
// 添加 Dagger 依賴
implementation 'com.google.dagger:dagger:2.28.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.28.3'
}
// 解析中文設置
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
```
### dagger 基礎注入 - 無參 constructor
| 註釋 | 功能 |
| -------- | -------- |
| @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 |
| @Component | 接口、橋樑,連接依賴 & 提供依賴方的 interface |
* Inject 標記是屬於 javax 包,也就是 dagger 是包裝了 java,以下是該註解的源碼,**運行在 Runtime**,並可以註釋在
^1^ 方法
^2^ 建構函數
^3^ 參數
```java=
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}
```
* 範例:Car 依賴於 Engine,所以在 Engine(依賴,也就是需要 **被注入**)上標示 @Inject
```java=
/**
* Car 依賴需求方,依賴了 Engine 類
*/
public class Car {
/**
* 1. 標記需要依賴的元素
* 2. 標記構造函數
*/
@Inject // -> 它就會尋找到相同標記的 Engine construct 並創建物件
Engine mEngine; // 不可為私有
public Car() {
DaggerICarComponent.builder().build().inject(this);
}
public Engine getEngine() {
return mEngine;
}
public static void main(String ... args){
Car car = new Car();
car.getEngine().start();
}
}
/**
* 依賴提供 & 依賴需求方的橋樑
*/
@Component
public interface ICarComponent {
void inject(Car car);
}
/**
* 提供依賴方
*/
public class Engine {
//標記 - 提供依賴的構造函數
@Inject
Engine() { }
public void start() {
System.out.println("Engine start run ~~");
}
}
```
**--實做結果--**
> 
:::warning
* Warning of inject
1. 只使用 `@Inject` & `@Component` 兩個註釋的話,提供依賴方的建構函數只能給予無參構造函數,**若是 inject 注入到有參數的 construct 則會錯誤**
2. 提供類中只能一個 inject 註釋在 construct 上
> 否則它會不確定要使用哪個建構函數注入
:::
### 有參 or 第三方庫注入
1. 有參構造函數:上面最後提到的 warning 中有說到 inject 只能注入無參 construct 這在使用上相當不方便,所以這邊有提出另外兩個註釋 ^1^ Module、^2^ Provides
| 註釋 | 功能 |
| -------- | -------- |
| @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 |
| @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 @Module** |
| - | - |
| @Module | 提供依賴方,**標示它是一個提供工廠** |
| @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**==Provides 是 ++具體提供++ 的產線==** |
:::warning
Component 可以指定多個 Module,在指定後透過 dagger 就會產生像對的程式
:::
```java=
/**
* 未何需要 Module 註釋?
* 1. 很多時候我們需要提供依賴的構造函數是第三方庫,我們沒辦法在第三方庫上的 construct 加上 @Inject 註釋,
* 而 CarModule 就作為一層隔離使用第三方庫
* 2. 提供的構造函數是有參數的,那就無法使用 @Inject 註釋構造參數
* 3. 需要注入的成員對象是抽象,則 @Inject 也無法使用
*/
@Module // 間接專用的工廠
public class CarModule {
public CarModule() { }
/**
* provider 用於 Module 被標注的類中,只能標住在方法之上
* 該方法在需要提供依賴時被調用
* 它會注入被 @Inject 註釋的 Member
*/
@Provides // 可當作間接標注
Engine provideEngine() {
return new Engine("Android Dagger Engine");
}
}
/**
* 依賴提供 & 依賴需求方的橋樑
*/
@Component(modules = {CarModule.class}) // 指定特定間接 Module 工廠
//@Component
public interface ICarComponent {
void inject(Car car);
}
```
:::danger
* 注入類必須是實體類,不可以是抽象類
```java=
public interface ICarComponent {
void inject(Car car);
}
```
:::
2. 第三方 Library:由於我們不可能去改動到 depend 的第三方 Library(除非整個專案拉下來重作,但這並不符合效益),所以我們必須多一個中間層
^1.^ 提供另一個 Module 工廠
^2.^ 規定 Component 橋樑指定 Module(提供依賴方)
```java=
public class MyGson {
@Inject
Gson mGson; // dagger 無法知道我們實際上有使用的類是哪個類
}
```
3. 抽象依賴:我們很常會使用到 OOP 的依賴倒置概念,但是 dagger 無法注入上層抽象(因為抽象不能創建,它也無法知道我們實際上有使用的類是哪個類)
```java=
public abstract class Engine {
public abstract void start();
}
public class Car {
@Inject
Engine mAndroidEngine; // dagger 無法知道我們實際上有使用的類是哪個類
}
```
---
* 依賴方的 inject 相同,但 Engine 並不會直接提供依賴,而是透過 Module + Provider(CarModule 類)提供依賴(以下為完整程式)
```java=
/**
* 不直接提供依賴
*/
public class Engine {
private String mBrand;
// @Inject // Err: 無法注入有參構造函數
Engine(String brand) {
mBrand = brand;
}
public void start() {
if(mBrand != null) {
System.out.println("Engine brand: " + mBrand);
}
System.out.println("Engine start run ~~");
}
}
/**
* 間接提供依賴給 Car,真正提供的地方
*/
public class CarModule {
public CarModule() { }
/**
* provider 用於 Module 被標注的類中,只能標住在方法之上
* 該方法在需要提供依賴時被調用
* 它會注入被 @Inject 註釋的 Member
*/
@Provides // 可當作間接標注
Engine provideEngine() {
return new Engine("Android Dagger Engine");
}
}
/**
* 依賴提供 & 依賴需求方的橋樑
*/
@Component(modules = {CarModule.class}) // 指定特定間接 Module 工廠
//@Component
public interface ICarComponent {
void inject(Car car);
}
/**
* Car 依賴需求方,依賴了 Engine 類
*/
public class Car {
@Inject // -> 它就會尋找到相同標記的 Engine construct 並創建物件
Engine mEngine;
public Car() {
DaggerICarComponent.builder()
.build()
.inject(this);
}
public Engine getEngine() {
return mEngine;
}
public static void main(String ... args){
Car car = new Car();
car.getEngine().start();
}
}
```
**--實做結果--**
> 
### 指定 Member 使用的對象
* 以下有 2 個狀況
1. 多個需要被注入類:若是依賴需求方有多個相同的對象,一般來說 dagger 會依照類型來做注入的動作,但是 **若要使用指定就必須使用 @Quialifier 註解**
```java=
// 以下在 car 中有多個相同類型的 Member,但是各自要求使用不同的 Engine,dagger 是無法區分的
@Inject
Engine mEngine;
@Inject
Engine mEngineWhite;
@Inject
Engine mEngineBlue;
```
2. 多個提供工廠:若是在 Module 中有多個提供依賴的 Provider 產線,dagger 就會分不清楚要使用哪個 Provider,最終導致編譯錯誤
```java=
@Module
public class CarModule {
public CarModule() { }
@Provides
Engine provideEngine1() {
return new Engine("Android Dagger 1 Engine");
}
@Provides
Engine provideEngine2() {
return new Engine("Android Dagger 2 Engine");
}
}
```
**--錯誤情況--**
> @Inject 被多次注入
>
> 
* 這時就可以使用到 Qualifier 註解,**Qualifier 主要是註解在註解之上,^1^首先創建你所需要的註解(假設為註解 A),並且標記該註解為 RUNTIME,^2^提供依賴方加上註解 A,^3^在依賴方加上相同的註解 A**
| 註釋 | 功能 |
| -------- | -------- |
| @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 |
| @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 Module** |
| - | - |
| @Module | 提供依賴方,**標示它是一個提供工廠** |
| @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**Provides 是 ++具體提供++ 的產線** |
| - | - |
| @Qualifier | 屬於 Javax 基礎注入,==**註解上的註解**==,用來區分注入成員 |
| @Named | 屬於 Javax 基礎注入,Javax 所提供的內置 String 型態註解 |
```java=
// 1. 創建註解
@Qualifier // 註解上的註解
@Retention(RetentionPolicy.RUNTIME)
public @interface EngineColor {
Color color() default Color.BLACK;
enum Color {
RED, WHITE, BLUE, BLACK, TAN
}
}
// 2. 提供方
@Engine.EngineColor(color = Engine.EngineColor.Color.WHITE)
@Provides
Engine provideEngineWhite() {
return new Engine("Android Engine of White !!!");
}
// 3. 需求方,同時加上限制
@Inject // 一般來說要注入哪個 Member 是透過該 Member 的類型決定
@Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應
Engine mEngineWhite;
```
:::success
* @Named 就是被 @Qualifier 註釋
```java=
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
```
:::
* 以下為 @Inject + @Providers + @Qualifier 完整範例程式
```java=
/**
* Car 依賴需求方,依賴了 Engine 類
*/
public class Car {
/**
* @Inject 有兩個功能
* 1. 標記需要依賴的元素
* 2. 標記構造函數
* Dagger 它就會尋找到相同標記的 Engine construct 並創建物件
*/
@Inject
Engine mEngine;
@Inject // 一般來說要注入哪個 Member 是透過該 Member 的類型決定
@Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應
Engine mEngineWhite;
@Inject // 這邊有兩個相同類型(都是 Engine)的 Member 則 Dagger 無法確定要注入哪個
@Engine.EngineColor(color = Engine.EngineColor.Color.BLUE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應
Engine mEngineBlue;
@Inject
@Named("Named") // 使用 String 分類
Engine mEngineNamed;
public Car() {
DaggerICarComponent.builder()
// .carModule(new CarModule()) // 可以不用
/**
* public ICarComponent build() {
* if (carModule == null) {
* this.carModule = new CarModule(); // 會自行創建
* }
* return new DaggerICarComponent(carModule);
* }
*/
.build()
.inject(this);
}
public Engine getEngine() {
return mEngine;
}
public Engine getWhiteEngine() {
return mEngineWhite;
}
public Engine getBlueEngine() {
return mEngineBlue;
}
public Engine getBlueNamed() {
return mEngineNamed;
}
public static void main(String ... args){
Car car = new Car();
car.getEngine().start();
car.getWhiteEngine().start();
car.getBlueEngine().start();
car.getBlueNamed().start();
}
}
/**
* 在提供方的 Module 加上相對的註解
*/
@Module
public class CarModule {
public CarModule() { }
/**
* provider 用於 Module 被標注的類中,只能標住在方法之上
* 該方法在需要提供依賴時被調用
* 它會注入被 @Inject 註釋的 Member
*/
@Provides
Engine provideEngine() {
return new Engine("Android Dagger Engine");
}
@Engine.EngineColor(color = Engine.EngineColor.Color.WHITE)
@Provides
Engine provideEngineWhite() {
return new Engine("Android Engine of White !!!");
}
@Engine.EngineColor(color = Engine.EngineColor.Color.BLUE)
@Provides
Engine provideEngineBlue() {
return new Engine("Android Engine of Blue !!!");
}
@Engine.EngineColor(color = Engine.EngineColor.Color.RED)
@Provides
Engine provideEngineRed() {
return new Engine("Android Engine of RED !!!");
}
@Named("Named")
@Provides
Engine provideEngineNamed() {
return new Engine("Android Engine of Named ???");
}
}
/**
* 自訂需要註解,並使用 Qualifier 註釋
*/
public class Engine {
@Qualifier // 註解上的註解
@Retention(RetentionPolicy.RUNTIME)
public @interface EngineColor {
Color color() default Color.BLACK;
enum Color {
RED, WHITE, BLUE, BLACK, TAN
}
}
private String mBrand;
//標記 construct (也可以標記 method, field)
@Inject // 一個類只能有一個 @Inject
Engine() { }
// @Inject // Err: 無法注入有參構造函數
Engine(String brand) {
mBrand = brand;
}
public void start() {
if(mBrand != null) {
System.out.println("Engine brand: " + mBrand);
}
System.out.println("Engine start run ~~");
}
}
/**
* 依賴提供 & 依賴需求方的橋樑
*/
@Component(modules = {CarModule.class})
public interface ICarComponent {
void inject(Car car);
}
```
**--實做結果--**
> 
### 範疇 @Scope
* 聽到 Scope 有學習過 C++ 的就會比較熟悉,其實 **@Scope 的意義範圍,在 dagger 中就是對注入的對象的限定,若是相同範圍的就使用同一個對象(也就是單例)**
| 註釋 | 功能 |
| -------- | -------- |
| @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 |
| @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 Module** |
| - | - |
| @Module | 提供依賴方,**標示它是一個提供工廠** |
| @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**Provides 是 ++具體提供++ 的產線** |
| - | - |
| @Qualifier | 屬於 Javax 基礎注入,**註解上的註解**,用來區分注入成員 |
| @Named | 屬於 Javax 基礎注入,Javax 所提供的內置 String 型態註解 |
| - | - |
| @Scope | 屬於 Javax 基礎注入,==**註解上的註解**==,可以實現出單例,包括局部、全局單例(一次 Dagger 就是一個對象),看使用者如何使用 |
| @Singleton | 屬於 Javax 基礎注入,Javax 所提供的內置 |
```java=
// 1. 實現自己的註釋
@Scope // 1. 定義局部單例限定,++Scope 本身不具有單例功能++,
// 每次注入都會新的對向,但對於單次注入就是單例
@Retention(RetentionPolicy.RUNTIME)
public @interface CarScope {
}
// 2. 註釋依賴提供方
@Engine.CarScope // 提供限定單例的實體
@Provides
public Engine provideEngine() {
return new Engine("Alien Brand");
}
// 3. Component 也需要註釋,代表提供的是單例對象
@Engine.CarScope // 提供限定單例的抽象
@Component(modules = {CarModule.class}) // 提供者 CarModule.class
public interface ICarComponent {
void injectEngine(Car car);
}
```
:::success
* @Singleton 就是被 @Scope 注入(@Scope 註解本身不具有單例功能)
```java=
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
```
:::
* 以下是完整程式 (使用 @Scope,而 @Singleton 只差異在不用自己定義註解)
```java=
/**
* 實際工廠
*/
public class Engine {
/**
* Scope 是註解上的註解
* 可以定義 "限定註解作用域" -> 實現局部單例 (有備該註解註解的才可以使用)
*/
@Scope // 1. 定義局部單例限定,Scope 本身不具有單例功能,每次注入都會新的對向,但對於單次注入就是單例
@Retention(RetentionPolicy.RUNTIME)
public @interface CarScope {
}
private String mBrand;
public Engine(String brand) {
mBrand = brand;
}
public void start() {
System.out.println(mBrand + ", Android Dagger Engine Running~ ~");
}
}
/**
* 間接工廠
*/
@Module
public class CarModule {
/**
* 2. 標記提供依賴方
* Singleton 只差異在不用自己定義註解
*/
@Engine.CarScope // 提供限定單例的實體
@Provides
public Engine provideEngine() {
return new Engine("Alien Brand");
}
}
/**
* 連接雙方的 Component 也需要註解
*/
@Engine.CarScope // 提供限定單例的抽象
@Component(modules = {CarModule.class})
public interface ICarComponent {
void injectEngine(Car car);
}
public class Car {
@Inject
Engine mEngineApple;
@Inject
Engine mEngineBanana;
public Car() {
// 這是一次的單例,若是在重新呼叫一次 build 則會是不同對象 !!! (因為會有新的對象)
DaggerICarComponent.builder()
.build()
.injectEngine(this);
}
public Engine getEngineApple() {
// 取到同一個對象
System.out.println("mEngineApple: " + mEngineApple.hashCode());
return mEngineApple;
}
public Engine getEngineBanana() {
// 取到同一個對象
System.out.println("mEngineBanana: " + mEngineBanana.hashCode());
return mEngineBanana;
}
public static void main(String[] args) {
Car car = new Car();
car.getEngineApple().start();
car.getEngineBanana().start();
}
}
```
**--實做結果--**
> 
### @Component 依賴
* **@Component 有一個方法是 dependencies**,它有點類似於一種註解的繼承(或是應該稱為依賴)
1. 先創建一個小的 Dagger 注入關係 (汽油),提供實體需要的 Gasoline 依賴
```java=
// 實體提供
public class Gasoline {
private int mType;
public Gasoline(int type) {
mType = type;
}
public void showGasoline() {
String gasoline;
if(mType == 0) {
gasoline = "Gasoline 95";
} else {
gasoline = "Gasoline 92";
}
System.out.println(gasoline);
}
}
// 代理提供
@Module
public class GasolineModule {
@Provides
public Gasoline gasolineProvider() {
return new Gasoline(1);
}
}
```
2. 創建較大的 Dagger 依賴關係 (引擎),提供實體需要的 Engine 依賴
```java=
// 實體提供
public class Engine {
private String mBrand;
public Engine(String brand) {
mBrand = brand;
}
public void start() {
System.out.println(mBrand + ", Android Dagger Engine Running~ ~");
}
}
// 代理提供
@Module
public class EngineModule {
@Provides
public Engine engineProvider() {
return new Engine("Alien");
}
}
```
3. Component 連結關係,並添加 dependencies 依賴關係
```java=
@Component( modules = {EngineModule.class},
dependencies = {IGasolineComponent.class}) // 依賴有點像是繼承、依賴關係
public interface ICarComponent {
void injectEngine(Car car);
}
```
4. 使用者就可以透過注入 Engine 類,並在內部再次注入 Gasoline 類
```java=
public class Car {
@Inject
Engine mEngine;
@Inject
Gasoline mGasoline;
public Car() {
// 成員注入
DaggerICarComponent.builder()
.iGasolineComponent(DaggerIGasolineComponent.builder().build()) // 沒注入就不可以使用
.build()
.injectEngine(this);
}
public Engine getEngine() {
return mEngine;
}
public Gasoline getGasoline() {
return mGasoline;
}
public static void main(String[] args) {
Car car = new Car();
car.getGasoline().showGasoline();
car.getEngine().start();
}
}
```
**--實做結果--**
> 
### Lazy 加載延遲
* 有時候可以使用加載延遲 Lazy<T\> 泛型類,這樣就可以省記憶體空間,在需要的時候再去獲取
```java=
// 以下就不提供 @Moduel @Component 程式,基本上與上面相同
public class Classmate {
@Inject
Lazy<Info> info;
public Classmate() {
DaggerIInfoComponent.builder()
.build()
.inject(this);
}
public Info getInfo() {
// 在需要時才取得數據
return info.get();
}
public static void main(String[] args) {
System.out.println(new Classmate().getInfo());
}
}
```
**--實做結果--**
> 
## Appendix & FAQ
:::info
:::
###### tags: `Android 進階` `Java 基礎進階`