У меня есть сервис RPC в GWT, который должен возвратить Список. Список может быть заполнен различными типами объектов, все из которых являются сериализуемыми и на весь из ссылаются в другом месте в моем сервисе, таким образом, они должны быть доступны RPC GWT. Однако, если я не поставил универсальный параметр типа (например. ArrayList<String>
), GWT дает мне предупреждение:
Return type: java.util.ArrayList java.util.ArrayList Verifying instantiability java.util.ArrayList [WARN] Checking all subtypes of Object which qualify for serialization` Adding '465' new generated units
По существу я просто хочу способ объявить Список или ArrayList без GWT, пытающегося сгенерировать код для каждого сериализуемого объекта на пути к классу. Разве там некоторый путь не состоит в том, чтобы сказать GWT, что я знаю то, что я делаю а не сходить с ума?
Позвольте мне подробнее рассказать о том, что сказал Дэвид Ноулс. Компилятор GWT не может читать ваши мысли, поэтому, когда вы не указываете, какими могут быть типы возврата, GWT предполагает, что это может быть что угодно, и должен сделать дополнительную работу, чтобы убедиться, что это может произойти на клиентской стороне Javascript.
Вы действительно должны указать, какие типы возврата могут быть. В этом есть только положительная сторона - компилятор будет производить более оптимизированный код, а не генерировать код для работы с единицами '465 genreated units', так что загрузка будет более быстрой.
Я бы предложил создать пустой интерфейс под названием "BaseResult", а затем, чтобы все возвращаемые объекты реализовали этот интерфейс.
/**
* Marker interface
*/
public interface BaseResult {
}
Затем вы указываете, что типом возвращаемого rpc-метода является ArrayList:
public interface MyRpcService extends RemoteService {
public ArrayList<BaseResult> doRpc();
}
Затем убедитесь, что все возвращаемые объекты реализуют этот интерфейс.
public class UserInfo implements BaseResult {}
public class Order implements BaseResult {}
Теперь компилятору GWT гораздо проще оптимизировать свой код.
Единственные подсказки, которые дает ошибка, это имя класса, и что что-то пошло ужасно неправильно во время инициализации этого класса. Так что либо в одном из этих статических инициализаторов, инициализация поля или, возможно, в вызываемом конструкторе.
Произошла вторая ошибка, поскольку класс не был инициализирован во время вызова A.getId (). Первая инициализация прервана. Обнаружение этой ошибки было хорошим тестом для инженерной команды; -)
Перспективным подходом для обнаружения такой ошибки является инициализация класса в тестовой среде и отладка кода инициализации (отдельных шагов). Тогда можно будет найти причину проблемы.
-121--3219304-Вот функция, преобразующая объект в плоский хэш
function flatten(json){
var nj = {},
walk = function(j){
var jp;
for(var prop in j){
jp = j[prop];
if(jp.toString() === "[object Object]"){
walk(jp);
}else{
nj[prop] = jp;
}
}
};
walk(json);
return nj;
}
-121--4407429- При добавлении поля ArrayList
или Object
в сериализуемый объект компилятор GWT не имеет выбора, кроме как включать все возможные Вы, по сути, объявляете Я могу отправить что угодно с помощью этого поля , поэтому компилятор проверяет, что вы можете отправить что-либо.
Решение состоит в объявлении, используя общие параметры, конкретных типов, которые вы отправляете. Для этого может потребоваться разделение на несколько параметров или классов, однако размер кода и время компиляции сохраняются.
Вам нужно будет помочь GWT, очень точно сообщив, что вы возвращаете. Типичное решение - использовать корневой класс или интерфейс маркера и объявить, что метод RPC возвращает ArrayList, после чего GWT может сократить возможные типы.
Меньше, чем желательно, иметь сериализаторы типов сборки компилятора GWT для всего, что находится под солнцем; в худшем случае это полностью выйдет из строя, потому что, например, может существовать класс (из, скажем, сторонней библиотеки GWT, которую вы используете), который объявляет тип в «клиентском» пакете, который реализует java.io.Serializable . Если вы попытаетесь использовать этот тип в своем коде, он станет частью пути к классам, который компилятор GWT анализирует для создания сериализатора типов; однако во время выполнения класс не является частью пути к классам на сервере, потому что тип был определен в «клиентском» пакете и, следовательно, не скомпилирован для сервера! Вызовы RPC, независимо от того, пытаются они использовать этот конкретный тип или нет, завершаются ошибкой с исключением ClassNotFound. Идеально!
Также, как сформулировано в плакате, невозможно заставить существующие примитивные типы реализовать некоторый интерфейс маркера, будь то IsSerializable или пользовательский интерфейс маркера (например, BaseResult, как предложено выше).
Тем не менее решение необходимо! Итак, вот что я придумал: 1) Используйте IsSerializable (или какой-либо его подкласс) вместо использования java.io.Serializable для всех ваших настраиваемых объектов передачи.
2) Используйте следующую реализацию RpcObject в тех случаях, когда вам нужен общий тип объекта для хранения значения, которое, как вы знаете, будет сериализуемым GWT-RPC (будь то один из ваших настраиваемых объектов передачи, реализующий IsSerializable, или другое "примитивный" тип, такой как java.lang.String [см. комментарии в реализации RpcObject ниже для тех типов, которые были внесены в белый список], который GWT уже знает, как сериализовать!)
Это решение работает для меня ... оно оба не позволяют GWT создавать сериализаторы типов для каждого класса java.io.Serializable под солнцем, в то же время позволяя мне как разработчику передавать значения, используя единый / унифицированный тип для примитивов (что я не могу добавить IsSerializable маркер в), а также мои собственные объекты передачи IsSerializable. Вот пример использования RpcObject (хотя использовать его так просто, мне кажется немного странным включать такие примеры):
RpcObject rpcObject = new RpcObject();
rpcObject.setValue("This is a test string");
Благодаря уловке java-generics метода getValue () приведение типов можно свести к минимуму , поэтому для получения значения (будь то на клиенте или на сервере) вы можете просто сделать следующее без какого-либо преобразования:
String value = rpcObject.getValue();
Вы можете так же легко передать один из ваших настраиваемых типов IsSerializable:
CustomDTO customDto= new CustomDTO(); // CustomDTO implements IsSerializable
customDto.setYourProperty(to_some_value);
RpcObject rpcObject = new RpcObject();
rpcObject.setValue(customDto);
И снова, позже на клиенте или сервере значение может быть легко извлечено (без преобразования):
CustomDTO customDto = rpcObject.getValue();
Вы можете так же легко обернуть что-нибудь вроде java.util.ArrayList:
List list = new ArrayList(); // Notice: no generics parameterization needed!
list.add("This is a string");
list.add(10);
list.add(new CustomDTO());
RpcObject rpcObject = new RpcObject();
rpcObject.setValue(list);
И снова, позже, в клиентском или серверном коде, вы можете вернуть список с помощью:
List list = rpcObject.getValue();
Посмотрев на «белый список» в RpcObject, вы можете быть склонны думать, что только Список
попал бы в белый список; вы ошибаетесь ;-) Пока все значения, добавленные в список List
, являются IsSerializable или объектами типов из JRE, которые GWT-RPC просто знает , как сериализовать, тогда вы все будет готово. Однако, если вам действительно нужно внести в белый список дополнительные типы, например тип из сторонней библиотеки, в которой используется java.io.Serializable вместо IsSerializable, может потребоваться индивидуальный белый список (подробности см. В реализации RpcObject) , они могут быть добавлены как новые поля непосредственно в RpcObject или, чтобы снизить накладные расходы в общих случаях, добавить их в подкласс RpcObject и использовать подкласс только при необходимости (поскольку это подкласс, ни один из ваших клиентов или серверов сигнатуры методов необходимо изменить с использования универсального типа RpcObject).
Я использую эту стратегию для решения проблем, почти идентичных описанным на оригинальном плакате. Я надеюсь, что и другие сочтут эту технику полезной, но, как всегда, ваш опыт может отличаться ... Если школа мысли GWT вышла за рамки этой техники, прокомментируйте и дайте мне знать!
-Джефф
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.rpc.IsSerializable;
public class RpcObject implements IsSerializable {
protected HashMap<String, IsSerializable> rpcObjectWrapper = new HashMap<String, IsSerializable>();
/*
* NOTE: The following fields are here to
* trick/fool/work-around/whatever-you-want-to-call-it GWT-RPC's
* serialization policy. Having these types present, even though their
* corresponding fields are never used directly, enables GWT-RPC to
* serialize/deserialize these primitive types if they are encountered in
* the rpcWrapperObject! Of course GWT-RPC already knows how to serialize
* all these primitive types, but since, for example, String doesn't
* implement GWT's IsSerializable interface, GWT has no expectation that it
* should ever be allowed in the rpcWrapperObject instance (and thus String,
* as well as all the other Java primitives plus Arrays of such types as
* well as List, Set, and Map, won't be part of the serialization policy of
* the RpcObject type). This is unfortunate because thanks to java type
* erasure, we can easily stuff Strings, Integers, etc into the wrapper
* without any issues; however, GWT-RPC will cowardly refuse to serialize
* them. Thankfully, it appears that the serialization policy is for the
* RpcObject type as a whole rather than for the rpcObjectWrapper field
* specifically. So, if we just add some dummy fields with these "primitive"
* types they will get added to the serialization policy (they are
* effectively white-listed) of the type as a whole, and alas, GWT-RPC stops
* cowardly refusing to serialize them.
*/
protected Byte _byte;
protected Short _short;
protected Integer _integer;
protected Long _long;
protected Float _float;
protected Double _double;
protected Date _date;
protected Boolean _boolean;
protected Byte[] _bytes;
protected Short[] _shorts;
protected Integer[] _integers;
protected Long[] _longs;
protected Float[] _floats;
protected Double[] _doubles;
protected Date[] _dates;
protected Boolean[] _booleans;
protected List<String> _list;
protected Set<String> _set;
protected Map<String, String> _map;
public RpcObject() {
super();
}
@SuppressWarnings("unchecked")
public <X> X getValue() {
HashMap h = (HashMap) rpcObjectWrapper;
X value = (X) h.get("value");
return value;
}
@SuppressWarnings("unchecked")
public void setValue(Object value) {
HashMap h = (HashMap) rpcObjectWrapper;
h.put("value", value);
}
}