2015年9月10日 星期四

透過 Guice 入門 Dependency Injection(二):動態選擇實作類別

作為某些方面可以替代重量級 Spring 的輕量級套件
Guice 可以透過程式碼的設定,動態決定注入介面時,要選擇的實作類別。



    動態選擇實作類別

    以下的範例延續前文,主要是在前文的程式碼上做修正。

    1、建立 Provider 以指派不同的實作類別
    假設我現在除了原有的 ImplementationA 以外,還有第二種實作類別 ImplementationB
    想要依照某個變數來決定要注入 ImplementationA 或 ImplementationB。
    選擇的方法是透過 Provider 來決定。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static class InterfaceProvider implements Provider<MyInterface> {
        @Override
        public MyInterface get() {
            switch (IMPL) {
                case "a":
                    return new ImplementationA();
                case "b":
                    return new ImplementationB();
                default:
                    return null;
            }
        }
    }

    這個 Provider 中,會依照 IMPL 這個全域的字串變數來決定實作類別是 ImplementationA 還是 ImplementationB。

    2、調整 configure()

    另一件要做的調整,是在 MyModule 類別中的 configure() 方法
    原本是利用 bind-to 的方法來指派注入的實作目標
    這裡要改為 bind-toProvider 的形式,也就是並不直接在 configure() 中指定目標
    而是指定要從 Provider 處取得。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
     * Configuring the injection of interfaces.
     */
    public static class MyModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(MyInterface.class).toProvider(InterfaceProvider.class);
        }
    }

    3、調整完的完整程式碼與執行結果

    綜合了(一)和上述的調整,調整後完整的程式碼如下,分為 Main.java 以及 RunningService.java 兩支程式。

    Main.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    package tw.twgogo.jimwayne.guice;
     
    import com.google.inject.AbstractModule;
    import com.google.inject.Guice;
    import com.google.inject.Injector;
    import com.google.inject.Provider;
     
    public class Main {
        private static String IMPL = "a";
         
        /**
         * Interface which can be injected automatically.
         */
        public static interface MyInterface {
            /**
             * Get the class name of the implementation.
             * @return
             */
            public String getClassName();
        }
         
        /**
         * An implementation for MyInterface.
         */
        public static class ImplementationA implements MyInterface {
            @Override
            public String getClassName() {
                return "ImplementationA";
            }
        }
         
        /**
         * An implementation for MyInterface.
         */
        public static class ImplementationB implements MyInterface {
            @Override
            public String getClassName() {
                return "ImplementationB";
            }
        }
         
        public static class InterfaceProvider implements Provider<MyInterface> {
            @Override
            public MyInterface get() {
                switch (IMPL) {
                    case "a":
                        return new ImplementationA();
                    case "b":
                        return new ImplementationB();
                    default:
                        return null;
                }
            }
        }
         
        /**
         * Configuring the injection of interfaces.
         */
        public static class MyModule extends AbstractModule {
            @Override
            protected void configure() {
                bind(MyInterface.class).toProvider(InterfaceProvider.class);
            }
        }
     
        public static void main(String[] args) {
            // Initiate the injection through Guice.
            Injector injector = Guice.createInjector(new MyModule());
             
            // Use Guice to inject and get the instance.
            IMPL = "a";
            System.out.println("Set IMPL to 'a'.");
            injector.getInstance(RunningService.class).print();
             
            IMPL = "b";
            System.out.println("Set IMPL to 'b'.");
            injector.getInstance(RunningService.class).print();
        }
    }

    其中上述程式碼最後的 main(),可以看到連續做了兩次取得 RunningService 的 instance
    並且呼叫 print() 方法,以觀察調整 IMPL 全域變數時,會對結果造成什麼影響。

    RunningService.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package tw.twgogo.jimwayne.guice;
     
    import tw.twgogo.jimwayne.guice.Main.MyInterface;
     
    import com.google.inject.Inject;
     
    public class RunningService {
        private MyInterface impl;
         
        @Inject
        public RunningService (MyInterface myInterface) {
            this.impl = myInterface;
        }
         
        public void print () {
            System.out.println("Implementation of my interface: " + this.impl.getClassName());
        }
    }

    執行結果如下:
    1
    2
    3
    4
    Set IMPL to 'a'.
    Implementation of my interface: ImplementationA
    Set IMPL to 'b'.
    Implementation of my interface: ImplementationB

    參考資料
    1. Guice: How to change injection on runtime based on a (dynamic web property)

    沒有留言: