GWT Динамическая загрузка с использованием GWT.create() со строковыми литералами вместо литератур класса

GWT.create() - эквивалент отражения в GWT, Но для класса требуется только классные литералы, а не полностью квалифицированные String. Как динамически создавать классы со строками, используя GWT.create()?

Это не возможно в соответствии со многими сообщениями форума GWT, но как это делается в таких системах, как Rocket-GWT (http://code.google.com/p/rocket-gwt/wiki/Ioc) и Gwittir (http://code.google.com/p/gwittir/wiki/Introspection)

Ответ 1

Возможно, хотя и сложно. Вот детали gory:

Если вы считаете GWT только прямой Java для JS, это не сработает. Однако, если вы рассматриваете генераторы - специальные классы с вашим компилятором GWT Compiles и Executes во время компиляции, это возможно. Таким образом, вы можете генерировать источник java и даже компилировать.

У меня была такая потребность сегодня. Наша система работает с динамическими ресурсами с Сервиса, заканчивая строкой и необходимостью для класса. Вот решение, к которому я придумал: btw, он работает под хостингом, IE и Firefox.

  • Создать модуль GWT, объявляющий:
    • Исходный путь
    • Генератор (который должен храниться вне пакета исходного пути модуля GWT)
    • Замена интерфейса (он будет вводить класс Generated вместо интерфейса)
  • Внутри этого пакета создайте интерфейс Marker (я называю Constructable). Генератор будет искать этот маркер
  • Создайте базовый абстрактный класс для хранения этого factory. Я делаю это, чтобы облегчить создание исходного кода
  • Объявите, что модуль наследует ваш Application.gwt.xml

Некоторые примечания:

  • К пониманию относится концепция генераторов;
  • Чтобы облегчить работу, базовый класс Аннотация пригодился.
  • Также поймите, что в сгенерированный источник .js и даже сгенерированный источник Java
  • Помните, что генератор выводит java файлы
  • GWT.create требует некоторой ссылки на файл .class. Этот вывод генератора может сделать это, если он каким-то образом ссылается на ваше приложение (проверьте, что Application.gwt.xml наследует ваш модуль, который также заменяет интерфейс генератором, объявленным вашим Application.gwt.xml).
  • Оберните вызов GWT.create внутри метода factory/singleton, а также под GWT.isClient()
  • Очень хорошая идея также обернуть вызовы кода-класса-загрузки вокруг GWT.runAsync, так как может потребоваться запуск загрузки модуля. Это ОЧЕНЬ важно.

Я надеюсь опубликовать исходный код в ближайшее время. Скрестите пальцы.:)

Ответ 2

Брайан,

Проблема заключается в том, что GWT.create не знает, как выбрать правильную реализацию для вашего абстрактного класса

У меня была аналогичная проблема с новым стилем кодирования GWT MVP (см. Документация GWT MVP)

Когда я позвонил:

ClientFactory clientFactory = GWT.create(ClientFactory.class);

Я получал ту же ошибку:

Тип результата отсроченной привязки 'com.test.mywebapp.client.ClientFactory' не должен быть абстрактным

Мне оставалось только добавить следующие строки в файл MyWebapp.gwt.xml:

<!-- Use ClientFactoryImpl by default -->
    <replace-with class="com.test.mywebapp.client.ClientFactoryImpl">
    <when-type-is class="com.test.mywebapp.client.ClientFactory"/>
    </replace-with>

Затем он работает как шарм

Ответ 3

Я столкнулся с этим сегодня и понял решение. Вопросник, по сути, хочет написать такой метод, как:

public <T extends MyInterface> T create(Class<T> clz) {
    return (T)GWT.create(clz);
}

Здесь MyInterface - это просто интерфейс маркера для определения диапазона классов, которые я хочу, чтобы динамически генерировать. Если вы попытаетесь скопировать код выше, вы получите сообщение об ошибке. Трюк состоит в том, чтобы определить "экземпляр", например:

public interface Instantiator {
    public <T extends MyInterface> T create(Class<T> clz);
}

Теперь определите генератор отложенной привязки GWT, который возвращает экземпляр выше. В генераторе запросите TypeOracle для получения всех типов MyInterface и создайте для них реализацию так же, как и для любого другого типа:

например:

public class InstantiatorGenerator extends Generator {

public String generate(...) {
   TypeOracle typeOracle = context.getTypeOracle();
   JClassType myTYpe= typeOracle.findType(MyInterface.class.getName());

    JClassType[] types = typeOracle.getTypes();
    List<JClassType> myInterfaceTypes = Collections.createArrayList();

    // Collect all my interface types.
    for (JClassType type : types) {
        if (type.isInterface() != null && type.isAssignableTo(myType)
                && type.equals(myType) == false) {
            myInterfaceTypes.add(type);
        }
        for (JClassType nestedType : type.getNestedTypes()) {
            if (nestedType.isInterface() != null && nestedType.isAssignableTo(myType)
                    && nestedType.equals(myTYpe) == false) {
                myInterfaceTypes.add(nestedType);
            }
        }
    }

    for (JClassType jClassType : myInterfaceTypes) {
        MyInterfaceGenerator generator = new MyInterfaceGenerator();
        generator.generate(logger, context, jClassType.getQualifiedSourceName());
    }
 }

 // Other instantiator generation code for if () else if () .. constructs as 
 // explained below.
}

Класс MyIntefaceGenerator аналогичен любому другому отложенному генератору привязки. За исключением того, что вы вызываете его непосредственно внутри указанного генератора, а не через GWT.create. После создания всех известных подтипов MyInterface (при создании подтипов MyInterface в генераторе убедитесь, что имя класса имеет уникальный шаблон, например MyInterface.class.getName() + "_MySpecialImpl" )., просто создайте Instantiator, повторив все известные подтипы MyInterface и создав связку

if (clz.getName().equals(MySpecialDerivativeOfMyInterface)) { return (T) new MySpecialDerivativeOfMyInterface_MySpecialImpl();}

стиль кода. Наконец, выведите исключение, чтобы вы могли вернуть значение во всех случаях.

Теперь, когда вы назовете GWT.create(clz);, сделайте следующее:

private static final Instantiator instantiator = GWT.create(Instantiator.class);
...
return instantiator.create(clz);

Также обратите внимание, что в вашем GWT-модуле xml вы определяете генератор для Instantiator, а не для генераторов MyInterface:

<generate-with class="package.rebind.InstantiatorGenerator">
    <when-type-assignable   class="package.impl.Instantiator" />
</generate-with>

Бинго!

Ответ 4

В чем именно вопрос - я предполагаю, что вы хотите передать параметры в дополнение к литералу класса генератору.

Как вы, вероятно, уже знаете, что литерал класса, переданный в GWT.create(), в основном является селектором, поэтому GWT может выбирать и выполнять генератор, который в конце выплескивает класс. Простой способ передать параметр генератору - использовать аннотации в интерфейсе и передать интерфейс .class в GWT.create(). Обратите внимание, конечно, что интерфейс/класс должен расширять литерал класса, переданный в GWT.create().

class Selector{
}

@Annotation("string parameter...")
class WithParameter extends Selector{}

Selector instance = GWT.create( WithParameter.class )

Ответ 5

Все возможно... хотя может быть сложно или даже бесполезно. Как сказал Ян, вы должны использовать генератор для этого. В принципе, вы можете создать свой интерфейс кода генератора, который берет этот интерфейс и компилируется во время создания и возвращает вам экземпляр. Примером может быть:

//A marker interface
public interface Instantiable {
}
//What you will put in GWT.create
public interface ReflectionService {
 public Instantiable newInstance(String className);
}
//gwt.xml, basically when GWT.create finds reflectionservice, use reflection generator
<generate-with class="...ReflectionGenerator" >
<when-type-assignable class="...ReflectionService" />
</generate-with>  
//In not a client package
public class ReflectionGenerator extends Generator{
...
}
//A class you may instantiate
public class foo implements Instantiable{
}
//And in this way
ReflectionService service = GWT.create(ReflectionService.class);
service.newInstance("foo");

Все, что вам нужно знать, это как сделать генератор. Я могу сказать вам, что в конце, что вы делаете в генераторе, нужно создать Java-код таким образом:

if ("clase1".equals(className)) return new clase1();
else if ("clase2".equals(className)) return new clase2();
...

В финале я подумал, что я могу сделать это вручную в виде InstanceFactory... С наилучшими пожеланиями

Ответ 6

Я смог сделать то, что, как я думаю, вы пытаетесь сделать, это загрузить класс и связать его с событием динамически; Я использовал генератор для динамической привязки класса к событию. Я не рекомендую это, но вот пример, если это помогает:

http://francisshanahan.com/index.php/2010/a-simple-gwt-generator-example/

Ответ 7

Не просмотрев код ракеты /gwittir (который вам следует делать, если вы хотите узнать, как они это сделали, в конце концов, это openource), я могу только догадываться, что они используют отложенную привязку таким образом что во время компиляции они обрабатывают все вызовы для отражения и статически генерируют весь код, необходимый для реализации этого вызова. Поэтому во время выполнения вы не можете делать разные.

Ответ 8

То, что вы пытаетесь сделать, невозможно в GWT.

В то время как GWT хорошо справляется с эмуляцией Java во время компиляции, время выполнения, конечно, совершенно иное. Большинство отражений не поддерживается, и невозможно создать или динамически загружать классы во время выполнения.

У меня был краткий взгляд на код для Gwittir, и я думаю, что они делают свой "материал отражения" во время компиляции. Здесь: http://code.google.com/p/gwittir/source/browse/trunk/gwittir-core/src/main/java/com/totsp/gwittir/rebind/beans/IntrospectorGenerator.java

Ответ 9

Возможно, вы сможете избежать всей проблемы, выполнив ее на стороне сервера. Произнесите услугу ведьма берет String и возвращает какой-то сериализуемый супер-тип. На стороне сервера вы можете сделать

return (MySerializableType)Class.forName("className").newInstance();

В зависимости от ваших обстоятельств это может быть не просто узкое место в производительности.