6

データをサーバーからクライアントに送信するためにシリアル化する必要がある不変クラス 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.
    . . .
}

Foo オブジェクトのインスタンス化は静的ファクトリ関数によって行われ、オブジェクトは不変であるため、引数がゼロのコンストラクターはありません。

データ メンバー Foo を含み、そのインスタンス化のために Builder パターンを実装する不変クラス Bar も検討してください (問題とは無関係であるため、スニペットから省略されています)。

public final class Bar
{
    private final Foo m_foo;
    . . .

    private Bar(final Builder builder)
    {
        . . .
    }

    public static Builder createBuilder()
    {
        return new Builder();
    }
}

不変性を削除したり、シリアル化のためにゼロ引数のコンストラクターを追加したりせずに、このオブジェクトをシリアル化する方法に関する私の選択を評価した後、CustomFieldSerializer を実装する必要があるという事実に結論付けました (クライアントとサーバーの両方)。

GWT のサーバー通信の記事に書かれているガイドラインに従い、以下に示すように独自の 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);
    }
}

クラス bar にはシリアライズが必要な Foo オブジェクトが含まれているため、別の一連の CustomFieldSerializers を実装しました。今回は、クライアントとサーバーの両方で同じパターンに従うクラス Foo 用です。

この問題は、クラス Bar のシリアル化が発生したときに、具体的にはこの時点で発生します。

public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance)
        throws SerializationException
{
   // . . .
   streamWriter.writeObject(instance.getFoo());
}

私が受け取る例外メッセージは次のとおりです。

[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.

カスタムシリアライザーがクライアントとサーバーの両方に提供されているにもかかわらず、 Foo クラスがホワイトリストに登録されたアイテムに属していないため、 writeObject()はタイプ Foo のオブジェクトをシリアライズできないようです。

いつでもwriteObject()の呼び出しをスキップして、Foo のデータ メンバーごとにwriteInt( )とwriteString() を呼び出すことができますが (これはうまく機能しています)、むしろwriteObject()を機能させたいと思います。私が提案した解決策は、Foo クラスの将来の変更が Foo のシリアライザー (明らか) と Bar のシリアライザー (それほど明白ではない) の両方に反映されなければならないため、メンテナンスの間違いが非常に起こりやすいです。

Foo と Bar の両方にisSerializableインターフェースを実装することから、ネット上で見つけることができるほとんどすべてを試しました(違いはありませんでしたし、独自のカスタムシリアライザーを提供する AFAIK クラスは必要ないため、違いはありません)このルールを順守する必要があります)、プライベートなゼロ引数コンストラクターを提供することさえあります (カスタム フィールド シリアライザーのインスタンス化関数が静的ファクトリを介してそれを処理する必要があるため、これも違いはありません)。

Foo クラスがホワイトリストに登録されていないのはなぜですか? 明らかな何かを見逃したり、何かを誤解したりしましたか?

よろしくお願いいたします。

4

1 に答える 1

6

ここでの問題は、コードのどこにも明示的に言及していない可能性が高く、そのFooクラスがサーバーに送信されることです。たとえば、次のようなサービスメソッドしかありません。

interface MyService {

   Foo getFoo(Bar bar); 

}

使用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

}

アプリでこのメソッドを呼び出したり、実装を提供したりする必要はありません。しかし、そうでなければそれは機能しません。GWTのこのヒントを作成する方法は他にもいくつかありますが、考え方は同じです。GWT-RPCのFooクラスを明示的に公開する必要があります。また、複数のサービスでクラスを使用する場合は、クラスを使用Barしている各サービスにそのようなメソッドを追加する必要があることにも注意してください。Bar

さて、これが起こる理由の詳細。サーバー側では、GWT-RPCは、シリアル化できるオブジェクトと逆シリアル化できるオブジェクトの2つのリストを追跡します。この情報は、RPCポリシーマニフェストから取得されます。私の最初の例ではBar、サーバーに送信できるものとしてオブジェクトについてのみ言及しました。ただし、クラスでカスタムフィールドシリアライザーを定義しているためBar、GWTは分析を実行しませんBar。したがって、のインスタンスがサーバーに送信される可能性があることはわかりません。したがって、サーバー側でFooデシリアライザーは必要ないと判断します。Fooしたがって、インスタンスをネットワーク経由で送信しよBarうとすると、サーバーはそれを逆シリアル化しようとしますが、readObjectカスタムシリアライザー内で使用され、Fooのデシリアライザーも検索しようとしますが、許可されていないため、非シリアル化プロセス全体が失敗します。オブジェクトのみをネットワーク経由で送信するサービスメソッドを追加するFooと、GWTはそのようなオブジェクトもサーバーに送信できることを認識し、Foo逆シリアル化可能としてマークします。

これは非常に紛らわしく、個人的には、カスタムシリアル化を使用したクラスをすべてのホワイトリストに自動的に追加する必要があると思いますが、それがその通りです。

編集

もう1つ(愚かな空のメソッドのない非ハッキーなソリューション)は、通信に特殊なDTOレイヤーを使用することです(たとえば、デフォルトのパブリックコンストラクターを持つPOJOのみ)が、クロスが多い複雑なオブジェクトグラフを送信する必要がある場合は難しい場合があります参照。

于 2013-02-27T22:42:27.303 に答える