2013-07-01 2 views
1

SHORT ANSWER: для привязки к объекту Object bind() должен быть предоставлен параметр Class < 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); 

LONG STORY:

У меня конфигурационные файлы в формате 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; 
} 

«S некоторый класс, который я хочу быть создан Guice, который оказывает параметр конструктора (необходимо вводить) типа DummyParams:

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

Теперь, так как DummyParams класса является то, если только во время выполнения (через JSon конфигурационный файл) и не может быть известно во время компиляции я не могу использовать этот тип в Guice привязках:

// 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: toInstance (захват # 189?) В com.google.inject.binder.LinkedBindingBuilder не может быть применен к (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); 
    } 
    } 
} 

Это аналогично решением Владимира, но с слепками неявными и сырыми типами вместо ссылок прямого объекта.

Я также написал новый способ разбора JSON с использованием методов класса Gava guava.

Смежные вопросы