2013-07-01 61 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的新方法。