Как я могу заставить GWT пытаться включить каждый сериализуемый класс, когда я использую ArrayList

У меня есть служба 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, что я знаю, что я делаю, и не сходить с ума?

Ответ 1

Позвольте мне рассказать о том, что сказал Давид Ноулз. Компилятор 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 будет намного легче оптимизировать время для вашего кода.

Ответ 2

Менее желательно иметь сериализаторы типа сборки компилятора 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<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 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

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

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

Ответ 4

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