Как я могу удержать GWT от попыток включить каждый сериализуемый класс, когда я использую ArrayList - PullRequest
11 голосов
/ 05 февраля 2010

У меня есть служба RPC в GWT, которая должна вернуть список.Список может быть заполнен различными типами объектов, все из которых являются сериализуемыми, и на них есть ссылки в другом месте моего сервиса, поэтому они должны быть доступны для GWT RPC.Однако, если я не добавлю параметр универсального типа (например, 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

По сути, я просто хочу объявить List или ArrayList без GWT, пытающегося генерировать коддля каждого сериализуемого объекта в пути класса.Разве нет способа сказать GWT, что я знаю, что делаю, и не сойти с ума?

Ответы [ 4 ]

5 голосов
/ 09 февраля 2010

Позвольте мне рассказать о том, что сказал Дэвид Нулс. Компилятор GWT не может прочитать ваши мысли, поэтому, когда вы не в состоянии указать, какими могут быть возвращаемые типы, GWT предполагает, что это может быть что угодно, и должен выполнить дополнительную работу, чтобы убедиться, что это может произойти на стороне клиента Javascript.

Вы действительно должны указать, какие типы могут быть возвращены. Это имеет только положительные стороны - поскольку компилятор будет генерировать более оптимизированный код, а не генерировать код для обработки «465 генерируемых единиц», ваши загрузки будут выполняться быстрее.

Я бы предложил создать пустой интерфейс с именем "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 будет намного проще оптимизировать ваш код.

4 голосов
/ 02 августа 2010

Менее желательно иметь сериализаторы типа сборки компилятора GWT для всего под солнцем; в худшем случае он полностью завершается сбоем, потому что, например, может существовать класс (например, используемая вами сторонняя библиотека GWT), который объявляет тип в пакете "client", который реализует java.io.Serializable. , Если вы попытаетесь использовать этот тип в своем коде, он станет частью пути к классам, который анализирует компилятор GWT для создания сериализатора типов; однако в время выполнения класс не является частью пути к классам на сервере, так как тип был определен в пакете "client" и поэтому не скомпилирован для сервера! Вызовы 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, вы можете склониться к мысли, что only List<String> был бы в белом списке; вы были бы неправы ;-) Пока все значения, добавленные к 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);
    }
}
3 голосов
/ 06 февраля 2010

Если вы добавите поле ArrayList или аналогичное Object к сериализуемому объекту, у компилятора GWT не останется иного выбора, кроме как включить все возможные варианты в его компиляцию. По сути, вы заявляете , что я могу отправить что угодно, используя это поле , поэтому компилятор гарантирует, что вы можете отправлять что угодно.

Решение состоит в том, чтобы объявить, используя общие параметры, конкретные типы, которые вы отправляете. Для этого может потребоваться разбиение на несколько параметров или классов, но это уменьшает размер кода и время компиляции.

1 голос
/ 06 февраля 2010

Вам нужно будет помочь GWT, очень точно указав, что вы возвращаете. Типичное решение - использовать корневой класс или интерфейс маркера и объявить, что метод RPC возвращает ArrayList, тогда GWT может обрезать возможные типы.

...