这是以前写的一篇东西,但一直没顾上整理。这两天整理了下。

公司的一个项目,最后决定用GWT来做后台界面,于是开始研究GWT。以前对GWT的了解也仅仅是写个HelloWorld看了下效果。这次项目后对GWT的机制以及使用中的问题整体梳理了一下。

一.Spring的整合

刚开始就遇到这个问题。怎么把goolge RPC Service 的服务器端交给Spring管理?找到个现成的开源项目做了这件事情-gwt-widget(http://gwt-widget.sourceforge.net/)的子项目gwt-sl(GWT Server Library)。 GWT的server端其实就是servlet,不过必须继承自RemoteServiceServlet。RemoteServiceServlet负责对GWT远程调用请求的解析以及输出的结果的序列化。gwt-sl提供一个GWTSpringController,它继承自RemoteServiceServlet并且实现了spring的Controller接口。所以只需要让自己的GWT RPC Service继承自GWTSpringController,并且当普通的Spring Controller配置就可以了。如果不喜欢继承的方式,gwt-sl还提供了spring的代理模式的解决方案。具体参看文档(http://gwt-widget.sourceforge.net/?q=node/51)。

二.界面控件的选择

GWT默认提供的控件太少,不能满足需求。还好,有第三方控件可选。
1.GWT-Ext (http://code.google.com/p/gwt-ext/)(http://gwt-ext.com/)
2.Ext GWT(GXT) (http://www.extjs.com/products/gxt/)

这两个项目都是把Ext的控件整合到了GWT中,项目的名字也及其相似,开始还把我搞糊涂了。gxt是Ext官方出的,不过看GWT-Ext的demo,丝毫不逊色于gxt。不过由于版权问题,GWT-Ext已经不更新了。最好别混用两个控件库,因为每个控件库的theme模式,事件模型,MVC架构,JavaBean的序列化方式之间都有区别。

三.用maven管理GWT项目
java项目,没有Maven怎么行?找到两个gwt的maven插件。
1.http://code.google.com/p/gwt-maven/ 2.http://mojo.codehaus.org/gwt-maven-plugin/

我们选择的是后者。GWT项目建立后,默认的web目录是在/war目录,和maven的maven-war-plugin(/src/main/webapp目录)冲突,所以需要修改下maven-war-plugin的配置。

<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.0.2</version>
            <configuration>
                <warSourceDirectory>war</warSourceDirectory>
            </configuration>
</plugin> 

四.GWT 客户端和服务器端交互需要注意的事项以及常见问题

1.客户端和服务器端交互传递的参数(RPC service的)都必须实现java.io.Serializable接口或者com.google.gwt.user.client.rpc.IsSerializable接口.

gwt并未使用java的序列化机制,仅仅使用Serializable接口作为一个标志,同时也便于兼容。但gwt的序列化机制会导致许多诡异的问题。

Type 'XXX' was not serializable and has no concrete serializable subtypes

这个异常一般是由于该类没实现序列化或者它的某个属性没有实现序列化。

2.Class 不能在远程调用方法中传递.因为gwt的class是有限功能的class。

3.

Type 'org.hibernate.collection.PersistentSet' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded.

如果直接将hibernate的ORM映射对象序列化了传递到gwt里去,并且使用了集合映射,就会出现这个错误。

4.

a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.  

这个错误有时候是客户端未刷新导致。

5.

Type XXX 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.

这个错误的原因是序列化对象未在白名单中。白名单是GWT编译器在编译java类的时候扫描java源码生成的,只有在白名单中的java类才能够在GWT中序列化。这种错误最常见的是声明了一个java的集合类,但没有用java泛型来设置该集合类中可能出现的类型,这样GWT编译器也无法把可能出现的类型添加到白名单里。当运行的时候,GWT遇到一个没在白名单的类型就不知道如何进行序列化了。
同理,GWT不允许使用Object声明类的属性。因为gwt的编译器会在编译的时候把所有需要序列化的类型以及该类型的子类型都加到白名单里。如果允许出现Object的话,那所有的类型都出现在白名单里了!所以为了效率,gwt中最好不要使用类层次较高的类型。

这个错误可以参看:

6.自定义序列化

GWT的序列化规则这么多,必要的时候需要自定义序列化的方式。
比如需要自定义 Game 类的序列化方式,就在Game的package目录下新建一个Game_CustomFieldSerializer类:

public class Game_CustomFieldSerializer {

    public static void deserialize(SerializationStreamReader streamReader, Game instance) throws SerializationException {
    }

    public static Game instantiate(SerializationStreamReader r) throws SerializationException {
        Game game = new Game();
        game.setName(r.readString());
        return game;
    }

    public static void serialize(SerializationStreamWriter streamWriter, Game g) throws SerializationException {
          streamWriter.writeString(g.getName());
    }
}

一般只需要实现instantiate 和 serialize 两个方法。必须保证写属性和读属性的顺序一样。

五.其他GWT的相关链接

1.GWT相关项目 http://www.infoq.com/cn/news/2010/04/GWT-Roundup