11

I have an RPC service in GWT that needs to return a List. The List can be filled with various types of objects, all of which are serializable and all of are referenced elsewhere in my service so they should be available to GWT RPC. However, unless I put on a generic type parameter (e.g. ArrayList<String>), GWT gives me the warning:

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

Essentially, I just want a way to declare List or ArrayList without GWT trying to generate code for every serializable object on the class path. Isn't there some way to tell GWT that I know what I'm doing and not to go crazy?

4

4 回答 4

5

让我扩展一下 David Nouls 所说的话。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 编译器将更容易优化您的代码。

于 2010-02-09T03:07:43.030 回答
4

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

正如海报所阐明的那样,不可能使现有的原始类型实现一些标记接口,无论它是 IsSerializable 还是自定义标记接口(例如上面建议的 BaseResult)。

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

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

这个解决方案对我有用......它既可以防止 GWT 为阳光下的每个 java.io.Serializable 类构建类型序列化器,同时又允许我作为开发人员使用单一/统一类型来传输值基元(我无法添加 IsSerializable 标记接口)以及我自己的自定义 IsSerializable 传输对象。下面是一个使用 RpcObject 的例子(虽然使用起来很简单,但是包含这样的例子我感觉有点奇怪):

RpcObject rpcObject = new RpcObject();
rpcObject.setValue("This is a test string");

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

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>被列入白名单;你会错的 ;-) 只要添加到的所有值都是 IsSerializable 或 GWT-RPC只知道List的 JRE 类型的对象如何序列化,然后你就准备好了。但是,如果您确实需要将其他类型列入白名单,例如使用 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);
    }
}
于 2010-08-02T06:31:36.330 回答
3

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

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

于 2010-02-05T22:01:51.483 回答
1

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

于 2010-02-06T18:10:26.327 回答