---
# System prepended metadata

title: Dagger 依賴注入
tags: [Java 基礎進階, Android 進階]

---

---
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 的汽油就必須在該類內修改，這樣**不符合開閉原則**，若是需要拓展也相當不方便

    **--實作--**
    > ![](https://i.imgur.com/JA1wF1H.png)

### 依賴倒置
* 先提到 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 中最常是配合反射一起使用
    
    > ![](https://i.imgur.com/qZyV7la.png)

### 依賴注入 - 注入方式
* 會關心到要如何注入 **抽象** 接口，有三個方法
    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";
            }
        }
        ```

    **--實作--**
    > ![reference link](https://i.imgur.com/l5ncs8q.png)

## 標準 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 ~~");
        }
    }
    ```
    **--實做結果--**
    > ![](https://i.imgur.com/qBeX7lK.png)

:::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();
        }
    }
    ```
    **--實做結果--**
    > ![](https://i.imgur.com/efOnkQr.png)

### 指定 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 被多次注入
    > 
    > ![](https://i.imgur.com/cKenzVd.png)

* 這時就可以使用到 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);
    }
    ```
    **--實做結果--**
    > ![](https://i.imgur.com/xAsIoTh.png)

### 範疇 @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();
        }
    }
    ```
    **--實做結果--**
    > ![](https://i.imgur.com/R9KYOea.png)

### @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();

            }
        }
        ```
    **--實做結果--**
    > ![](https://i.imgur.com/73CAUrR.png)

### 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());
        }
    }
    ```
    **--實做結果--**
    > ![](https://i.imgur.com/BVeElbd.png)

## Appendix & FAQ

:::info
:::

###### tags: `Android 進階` `Java 基礎進階`
