2013-07-01 59 views
1

簡短回答:綁定到Object實例的bind()方法應該與類< Object>參數一起提供。 這就是說:Guice - 如何綁定程序運行時確定的類型?

Class<?> type = got_a_type(); Object object = got_an_object(); 
// Illegal - compilation error because of type check comparing ? to Object 
bind(type).toInstance(object); 
// Legal and working 
bind((Class<Object>)type).toInstance(object); 

很長的故事:

我在下面的格式從舊系統中的JSON配置文件:

{ 
    "$type": "test_config.DummyParams", 
    "$object": { 
     "stringParam": "This is a string", 
     "integerParam": 1234, 
     "booleanParam": false 
    } 
} 

的test_config.DummyParams程序中是可用的類運行時,看起來像這樣:

package test_config; 

public class DummyParams { 
    public String stringParam; 
    public int integerParam; 
    public boolean booleanParam; 
} 

There的一些類,我想通過其爲具有構造函數的參數吉斯創建(需要注入)DummyParams類型:

@Inject 
public class DummyService(DummyParams params) { ... } 

現在,由於DummyParams類是僅在運行時提供的東西(通過JSON配置文件),不能在編譯的時候知道我不能使用這種類型的吉斯綁定:

// Can't do this because DummyParams type should come from config file 
Object object = ...; // Getting object somehow 
bind(DummyParams.class).toInstance((DummyParams)object); 

我有一些舊的代碼,這使我對讀取類和對象(類和實例)的所有json配置文件:

class ConfigObject { 
    Class<?> type; 
    Object instance; 
} 

我試圖簡單地將它們綁定:

ConfigObject obj = config.read(); // Getting pairs from config files walker 
bind(obj.type).toInstance(obj.instance); 

但是,這並不編譯:「Java的:在com.google.inject.binder.LinkedBindingBuilder toInstance(?捕獲#189)不能被應用到(java.lang.Object中)」。

所以,這裏有一個問題:如何綁定在運行時確定的類型的實例?我打破IoC的概念,並且正在做我想做的事情嗎?

回答

3

我想你可以在這裏安全地使用類型轉換。下面是一個完整的例子:

public class Main { 
    public static void main(String[] args) { 
     final Holder holder = new Holder("abcd", String.class); 

     Injector injector = Guice.createInjector(new AbstractModule() { 
      @Override 
      protected void configure() { 
       bind((Class<Object>) holder.type).toInstance(holder.instance); 
      } 
     }); 

     System.out.println(injector.getInstance(String.class)); // Prints "abcd" 
    } 

    public static class Holder { 
     public final Class<?> type; 
     public final Object instance; 

     public Holder(Object instance, Class<?> type) { 
      this.instance = instance; 
      this.type = type; 
     } 
    } 
} 

或者你可以將這個問題向上級和改變你的配置對象包含Class<Object>;那麼你將不得不投在你的配置閱讀器類:

public class Main { 
    public static void main(String[] args) { 
     final Holder holder = new Holder("abcd", (Class<Object>) (Class<?>) String.class); 

     Injector injector = Guice.createInjector(new AbstractModule() { 
      @Override 
      protected void configure() { 
       bind(holder.type).toInstance(holder.instance); 
      } 
     }); 

     System.out.println(injector.getInstance(String.class)); 
    } 

    public static class Holder { 
     public final Class<Object> type; 
     public final Object instance; 

     public Holder(Object instance, Class<Object> type) { 
      this.instance = instance; 
      this.type = type; 
     } 
    } 
} 

這需要通過通配符類型或類似這樣的東西鑄造,雖然。

但是,整個情況真的很奇怪。如果你的配置類沒有共同的超類型,它們將如何被使用呢?如果你事先不知道你的對象屬於哪一類,那麼你幾乎不能(沒有反射)或者使用instanceof s。

1

使用和原材料的類型,隱選中鑄件和一些番石榴庫的濫用:

public class RuntimeParams { 
    public static void main(String[] args) throws ClassNotFoundException, IOException { 

    // reads from config and bind in runtime 
    final ConfigReader config = new ConfigReader(); 
    final ConfigObject configObject = config.read(); 
    final Injector injector = Guice.createInjector(new AbstractModule() { 
     @Override protected void configure() { 
     bind(configObject.type).toInstance(configObject.instance); 
     } 
    }); 

    // use the binded instance 
    final DummyParams instance = injector.getInstance(DummyParams.class); 
    System.out.println("DummyParams' stringParam: " + instance.stringParam + "\nDummyParams' integerParam: " 
     + instance.integerParam + "\nDummyParams' booleanParam: " + instance.booleanParam); 
    } 
} 

class ConfigObject<T> { 
    private static final Gson gson = new Gson(); 

    final T instance; 
    final Class<T> type; 

    ConfigObject(final String json, final Class<T> type) { 
    this.instance = gson.fromJson(json, type); 
    this.type = type; 
    } 
} 

class ConfigReader { 
    private static final Gson gson = new Gson(); 

    ConfigObject read() throws ClassNotFoundException, IOException { 
    try (final FileReader reader = new FileReader(new File("src/main/resources/runtimeClazzNParams.json"));) { 
     final Map<String, Object> configMap = gson.fromJson(reader, Types.mapOf(String.class, Object.class)); 
     final Class<?> clazz = Class.forName((String) configMap.get("$type")); 
     return new ConfigObject<>(gson.toJson(configMap.get("$object")), clazz); 
    } 
    } 
} 

它類似於弗拉基米爾的解決方案,但與石膏隱式和原料類型而不是直接對象引用。

我還寫了一種使用Gson番石榴類方法解析JSON的新方法。