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();
В зависимости от ваших обстоятельств это может быть не просто узкое место в производительности.