Serialization 当我使用ArrayList时,如何防止GWT尝试包含每个可序列化类

Serialization 当我使用ArrayList时,如何防止GWT尝试包含每个可序列化类,serialization,gwt,gwt-rpc,Serialization,Gwt,Gwt Rpc,我在GWT中有一个需要返回列表的RPC服务。列表中可以填充各种类型的对象,所有这些对象都是可序列化的,并且所有对象都在我的服务中的其他位置引用,因此GWT RPC应该可以使用它们。但是,除非我添加了泛型类型参数(例如,ArrayList),否则GWT会向我发出警告: Return type: java.util.ArrayList java.util.ArrayList Verifying instantiability java.util.ArrayLis

我在GWT中有一个需要返回列表的RPC服务。列表中可以填充各种类型的对象,所有这些对象都是可序列化的,并且所有对象都在我的服务中的其他位置引用,因此GWT RPC应该可以使用它们。但是,除非我添加了泛型类型参数(例如,
ArrayList
),否则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 返回类型:java.util.ArrayList java.util.ArrayList 验证可实例化性 java.util.ArrayList [警告]正在检查符合序列化条件的对象的所有子类型` 添加“465”新生成的单位
本质上,我只是想要一种声明List或ArrayList的方法,而不需要GWT为类路径上的每个可序列化对象生成代码。有没有办法告诉GWT我知道我在做什么,不要发疯?

如果向可序列化对象添加
ArrayList
或类似的
对象
字段,GWT编译器别无选择,只能在编译中包含所有可能的变体。实际上,您声明我可以使用此字段发送任何内容,因此编译器确保您能够发送任何内容


解决方案是使用泛型参数声明要发送的特定类型。这可能需要拆分为多个参数或类,但它确实会降低代码大小和编译时间。

您必须通过非常精确地返回内容来帮助GWT。一个典型的解决方案是使用根类或标记接口并声明RPC方法返回ArrayList,然后GWT可以缩减可能的类型。

让我详细介绍一下David Nouls所说的内容。GWT编译器无法读懂您的心思,因此当您无法指定返回类型时,GWT会假定它可以是任何类型,并且必须做额外的工作以确保在Javascript客户端上可以实现

您确实应该指定能够返回的类型。这样做只有好处——因为编译器将生成更多优化的代码,而不是生成处理“465个genreated units”的代码,所以您的下载速度会更快

我建议创建一个名为“BaseResult”的空接口,然后让您返回的所有对象都实现该接口

/**
 * Marker interface 
 */
public interface BaseResult {
}
public class UserInfo implements BaseResult {}
public class Order implements BaseResult {}
然后指定rpc方法的返回类型为ArrayList:

public interface MyRpcService extends RemoteService {
  public ArrayList<BaseResult> doRpc();
}
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);

现在,GWT编译器将更容易为您的代码进行优化。

让GWT编译器为所有内容构建类型序列化程序是不可取的;在最坏的情况下,它完全失败是因为,例如,可能有一个类(比如说,来自您正在使用的第三方GWT库)在实现java.io.Serializable的“client”包中声明了一个类型。如果您试图在代码中使用该类型,它将成为GWT编译器分析以构建类型序列化程序的类路径的一部分;但是,在运行时,该类不是服务器上的类路径的一部分,因为该类型是在“客户端”包中定义的,因此没有为服务器编译!RPC调用,无论它们是否尝试使用该特定类型,都会失败,并出现ClassNotFound异常。太好了

正如海报所阐述的,无论是可序列化的还是自定义的标记接口(如上面建议的BaseResult),都不可能使现有的基本类型实现某些标记接口

尽管如此,还是需要一个解决方案!下面是我的想法: 1) 在所有自定义传输对象上使用IsSerializable(或其某些子类),而不是使用java.io.Serializable

2) 在需要通用对象类型来保存已知可GWT-RPC序列化的值(无论是实现IsSerializable的自定义传输对象之一,还是更“原始”的类型,如java.lang.String)的情况下,使用以下RpcObject实现[请参阅下面RpcObject实现中的注释,了解那些已被列入白名单的类型]GWT已经知道如何序列化!)

这个解决方案对我来说是可行的……它既可以防止GWT为sun下的每个java.io.Serializable类构建类型序列化程序,同时也允许我作为开发人员使用单个/统一的原语类型来传递值(我无法将IsSerializable marker接口添加到其中)以及我自己定制的IsSerializable transfer对象。下面是一个使用RpcObject的示例(虽然使用它很简单,但我对包含这样的示例感到有点奇怪):

由于getValue()方法的java泛型技巧,可以将强制转换保持在最低限度,因此要检索值(无论是在客户端还是服务器上),您只需执行以下操作,而无需强制转换:

String value = rpcObject.getValue();
您可以轻松地传输一种自定义IsSerializable类型:

CustomDTO customDto= new CustomDTO(); // CustomDTO implements IsSerializable
customDto.setYourProperty(to_some_value);
RpcObject rpcObject = new RpcObject();
rpcObject.setValue(customDto);
同样,稍后在客户端或服务器上,可以轻松获取值(无需强制转换):

您可以同样轻松地包装java.util.ArrayList:

public interface MyRpcService extends RemoteService {
  public ArrayList<BaseResult> doRpc();
}
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中,您可能倾向于认为只有
列表
会被白名单;您可能会错;-)只要添加到
列表
的所有值都是可序列化的,或者是GWT-RPC只知道如何序列化的JRE类型的对象,那么您就可以全部设置。但是,如果您确实需要白名单额外的值类型,例如,使用java.io.Serializable而不是IsSerializable的第三方库中的类型可能需要单独列出(有关详细信息,请参阅RpcObject的实现),它们可以直接作为新字段添加到RpcObject中,或者为了在常见情况下降低开销,将它们添加到RpcObject的子类中,并仅在需要时使用该子类(因为它
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);
    }
}