hea*_*ent 6 java gwt serialization
考虑一个不可变类Foo(一个由ID和名称组成的POJO),需要对其进行序列化,以便将数据从服务器发送到客户端.
public final class Foo
{
    private final int m_id;
    private final String m_displayName;
    private Foo(final int id, final String displayName)
    {
        m_id = id;
        m_displayName = displayName;
    }
    public static Foo create(final int id, final String displayName)
    {
         // Some error checking occurs here.
         . . . 
         m_id = id;
         m_displayName = displayName;
    }
    // Getters etc.
    . . .
}
Run Code Online (Sandbox Code Playgroud)
Foo对象的实例化通过静态工厂函数发生,并且由于对象是不可变的,因此没有零参数构造函数.
考虑一个不可变类Bar,它包含一个数据成员Foo,并为其实例化实现Builder模式(从代码片段中省略,因为它与问题无关).
public final class Bar
{
    private final Foo m_foo;
    . . .
    private Bar(final Builder builder)
    {
        . . .
    }
    public static Builder createBuilder()
    {
        return new Builder();
    }
}
Run Code Online (Sandbox Code Playgroud)
在评估了我应该序列化这个对象的方式而没有删除它的不变性或者为了序列化而添加任何零参数构造函数的选择后,我得出结论我需要实现一个CustomFieldSerializer(对于客户端和服务器).
我遵循GWT 的Server Communication文章中编写的指南,并实现了我自己的CustomFieldSerializer,如下所示.
// Contains the serialization logic of the class Bar.
public final class Bar_CustomFieldSerializerBase
{
    public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException
    {
        return Bar.createBuilder().forFoo((Foo) streamReader.readObject()).build();
    }
    public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance)
        throws SerializationException
    {
        // . . .
        streamWriter.writeObject(instance.getFoo());
    }
    public static void deserialize(final SerializationStreamReader streamReader, final Bar instance)
    {
        /*
         * Empty as everything is handled on instantiateInstance().
         */
    }
}
// The CustomFieldSerializer for class Bar. 
public class Bar_CustomFieldSerializer extends CustomFieldSerializer<Bar>
{
    public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException
    {
    Bar_CustomFieldSerializerBase.deserialize(streamReader, instance);
    }
    public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException
    {
        Bar_CustomFieldSerializerBase.serialize(streamWriter, instance);
    }
    public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException
    {
        return Bar_CustomFieldSerializerBase.instantiate(streamReader);
    }
    @Override
    public boolean hasCustomInstantiateInstance()
    {
        return true;
    }
    @Override
    public Bar instantiateInstance(final SerializationStreamReader streamReader) throws SerializationException
    {
        return instantiate(streamReader);
    }
    @Override
    public void deserializeInstance(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException
    {
        deserialize(streamReader, instance);
    }
    @Override
    public void serializeInstance(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException
    {
        serialize(streamWriter, instance);
    }
// Server side CustomFieldSerializer for class Bar.
public class Bar_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Bar>
{
    public static void deserialize(ServerSerializationStreamReader streamReader, Bar instance,
        Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException
    {
    /*
     * Empty as everything is handled on instantiateInstance().
     */
    }
    @Override
    public Bar instantiateInstance(ServerSerializationStreamReader streamReader) throws SerializationException
    {
        return Bar_CustomFieldSerializerBase.instantiate(streamReader);
    }
    @Override
    public void deserializeInstance(ServerSerializationStreamReader streamReader, Bar instance,
        Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException
    {
        deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
    }
    @Override
    public void deserializeInstance(SerializationStreamReader streamReader, Bar instance) throws SerializationException
    {
        Bar_CustomFieldSerializerBase.deserialize(streamReader, instance);
    }
    @Override
    public void serializeInstance(SerializationStreamWriter streamWriter, Bar instance) throws SerializationException
    {
        Bar_CustomFieldSerializerBase.serialize(streamWriter, instance);
    }
}
Run Code Online (Sandbox Code Playgroud)
由于类栏包含需要序列化的Foo对象,因此我继续实现另一组CustomFieldSerializers,这次是针对客户端和服务器的类Foo遵循相同的模式.
当类Bar发生序列化时会出现问题,特别是此时:
public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance)
        throws SerializationException
{
   // . . .
   streamWriter.writeObject(instance.getFoo());
}
Run Code Online (Sandbox Code Playgroud)
我得到的异常消息如下:
[WARN] Exception while dispatching incoming RPC call com.google.gwt.user.client.rpc.SerializationException: Type 'ui.shared.models.fooItems.Foo' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
Run Code Online (Sandbox Code Playgroud)
似乎writeObject()无法序列化Foo类型的对象,因为Foo类不属于列入白名单的项目,即使已为客户端和服务器提供了自定义序列化程序.
我总是可以跳过writeObject()调用并为每个Foo的数据成员调用writeInt()和writeString()(这很好),但我更愿意让writeObject()工作.我提出的解决方案很容易出现维护错误,因为未来Foo类中的任何更改都必须反映在Foo的序列化器(显而易见的)和Bar的序列化器(不那么明显)上.
我已经尝试了几乎所有我能在网上找到的东西,从在Foo和Bar上实现isSerializable接口(没有任何区别,它应该没有任何区别,因为提供自己的自定义序列化器的AFAIK类不需要遵守这个规则)甚至提供一个私有零参数构造函数(这也应该没有任何区别,因为自定义字段序列化程序的实例化函数应该通过静态工厂来处理).
为什么Foo类没有列入白名单?我是否错过了一些明显或错误解释的东西?
感谢您提前的时间.
那么问题在于,很可能你从未明确提到代码中的任何地方,Foo该类曾被发送到服务器.例如,您只有这样的服务方法:
interface MyService {
   Foo getFoo(Bar bar); 
}
Run Code Online (Sandbox Code Playgroud)
要使用writeObject你需要显式地为GWT做一个提示Foo,通过添加一个新的服务方法将Foo类包含到反序列化列表中,该方法将类作为参数:
interface MyService {
   Foo getFoo(Bar bar); 
   void setFoo(Foo foo);// letting GWT know that we might send Foo object over the wire, you don't have to ever call this method in your app, or implement it in some meaningful way, just let it be there
}
Run Code Online (Sandbox Code Playgroud)
您不必在应用程序中调用此方法,也不必为其提供任何实现.但否则它将无法正常工作.如何为GWT创建这个提示的方法很少,但是想法是一样的,你将明确地为GWT-RPC公开Foo类.还要记住,如果您Bar在多个服务中使用类,则必须将此类方法添加到使用Bar类的每个服务
现在,详细说明为什么会这样.在服务器端,GWT-RPC跟踪两个列表:它可以序列化的对象和可以反序列化的对象.此信息来自RPC策略清单.在我的第一个例子中,我只提到了Bar对象作为可以发送到服务器的东西.但是由于您已经在Bar类上定义了自定义字段序列化程序,GWT不会对其执行任何分析Bar,因此它不知道Foo可能会将实例发送到服务器,因此它决定Foo服务器端不需要反序列化程序.因此,当您尝试Bar通过线路发送实例时,服务器会尝试对其进行反序列化,但由于readObject在自定义序列化器中使用,它也会尝试为Foo查找反序列化器,但是不允许这样做,因此整个deserealization过程失败.如果添加额外的服务方法,它只Foo通过线路发送对象,GWT也会意识到这样的对象也可以发送到服务器,因此它也标记Foo为可反序列化.
这非常令人困惑,我个人认为自定义序列化的类应该自动添加到所有白名单,但它就是这样.
编辑
另一个(没有愚蠢的空方法的非hacky解决方案)将使用专门的DTO层进行通信(例如,只使用具有默认公共构造函数的POJO),但是当您需要发送具有大量交叉的复杂对象图时,可能会很困难引用.
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           3691 次  |  
        
|   最近记录:  |