可以看出,除了多了一个 AsyncCallback 对象作为参数,此异步接口与同步接口的方法大致相同。此外,该方法的返回值被定义成 void,原因是在 GWT 的异步通信机制中,客户端发送的异步请求信息和服务器端对此请求的回应信息(包括成功或失败)都会通过这个 AsyncCallback 对象来传递。换句话说,这个对象将客户端与服务器端绑定了起来,返回值在这里没有什么作用。除此之外,此异步接口的命名规则是在原接口名字的后面加上 Async 后缀,并放置在客户端的同一个包下面。
3. 实现这个接口。大家知道,所有的服务端接受到 Service 后都要执行一定的操作来响应客户端的请求。这里,所有的 RPC 服务都要扩展自 RemoteServiceServlet 并实现相应的 Service 接口。需要注意的是,它并不需要实现此 Service 的异步接口。
Public MyServiceImpl extends RemoteServiceServlet implements MyService{ Public List myRequestToService(int requestParameters){ … .// 实现的功能代码 } } |
其中,RemoteServiceServlet 类用于处理客户端请求的反序列化和相应的序列化以及调用接口中定义的方法。 GWT 之所以采用这种基于 Servlet 的处理方式,是因为 Java Servlet 相对简单且广泛流行于 Java Community 。这样,我们所定义的服务及实现就可以轻易的重新部署到其它产品的 Servlet 容器中。
4. 在客户端触发服务。在 GWT 框架下调用 Service 总是经过相同的步骤:
- 使用 GWT.create()初始化 Service 接口 ;
- 使用 ServiceDefTarget 为 Service 代理描述 Service 接入点的 URL ;
- 创建一个异步回调对象,用于 RPC 完成时调用;
- 发出 RPC 请求。
5. 将此服务作为一个 EntryPoint 加入到应用程序的配置文件 module.xml 中,以便客户端能够找到此服务。
<Servlet path= ” /myService ” class= ” examples.server.MyServiceImpl ” /> |
以上五步是在 GWT 框架下开发异步通信应用的流程。而在实际开发中,我们可以利用一些 Eclipse 的插件帮助我们快速地完成此过程的搭建。接下来,将介绍一个异步通信的例子并用 Eclipse 下的 GWT 插件 Cypal Studio 来完成创建的过程。
这是一个简单的做两位数加法的例子。用户在输入框中输入两个整型数后,不必刷新整个页面,Server 端就会返回结果。
图 5. 做两位数加法的例子
为了完成这个例子,首先在一个 Panel 中创建两个 TextField 和两个 Label 。
TextField textField1 = new TextField(); Label addLabel = new Label( “ + ” ); TextField textField2 = new TextField(); Label equalLabe2 = new Label( “ = ” ); |
然后,通过 Cypal Studio 插件来创建计算两个数加法的服务。 Cypal Studio 是 GWT 在 Eclipse 中的一个插件,它可用于简化在 GWT 开发过程中执行的许多常见任务。在安装 Cypal Studio 插件之前,您需要配有 Web Tools Platform(WTP) 插件的 Eclipse 版本。 WTP 是支持 Web 应用开发的精选工具集,它包括各种标准的 Web 编辑器,比如 HTML 和层叠样式表(Cascading Style Sheet,CSS)、JavaServer Page(JSP) 编辑器,支持创建和维护 Web 应用程序中使用的数据库,以及开发过程中在 Web 服务器上运行应用程序。因此,Cypal Studio For GWT 插件需要 WTP 才能运行。
这里,对如何安装 WTP 和 Cypal Studio For GWT 插件不介绍,用户可以参考以前的系列文章。
在安装 Cypal Studio For GWT 插件后,可以在 Eclipse 的 Window->Preferences 中找到它。
图 6. Cypal Studio For GWT 配置选项
在 Cypal Studio 配置窗口中,可以配置 GWT 的 Home 路径以及 GWT 编译后的 OutPut 路径。
现在,我们就应用 Cypal Studio 插件来创建我们服务。首先,点击 Eclipse 的 File->New->Other …菜单,选择 GWT Remote Service,如图 7 所示。
图 7. 选择向导的类型
然后,依次指定应用程序的配置文件路径,服务的名字,此服务的链接地址(Service URL)。
图 8. 指定配置文件路径,服务的名字和链接地址
设置好之后,点击 Finish 。你会发现此插件为我们自动加入了 CalcuService.java,CalcuServiceAsync.java, CalcuServiceImpl.java,
图 9. CalcuService.java,CalcuServiceAsync.java, CalcuServiceImpl.java 已加入工作空间
以及此服务的配置信息。
图 10. 服务配置信息
而我们所需要做的就是在 CalcuService 接口中定义方法以及在 CalcuServiceImpl 中实现此方法,如代码所示:
清单 1. 定义和实现 getAddResult 方法
public interface CalcuService extends RemoteService { String getAddResult(String str1, String str2); } public class CalcuServiceImpl extends RemoteServiceServlet implements CalcuService { public String getAddResult(String str1, String str2) { // TODO Auto-generated method stub int result = new Integer(str1).intValue() + new Integer(str2).intValue(); return result + ""; } } |
另外,Cypal Studio For GWT 插件会自动监控接口中方法的变化。比如,我们在 CalcuService 中添加一个 getAddResult(String Str1,String Str2) 方法后,它会自动帮助我们在 CalcuServiceAsync 异步接口中添加这个方法。
public interface CalcuServiceAsync { void getAddResult(String str1, String str2, AsyncCallback<String> callback); } |
在定义好接口和实现后,接下来要做的就是定义触发此服务的方法。
示例中,一旦我们分别在两个输入框中输入整型数后,就会异步地向服务端发送请求。因此,需要为这两个输入框定义监听事件。
这里,我们定义一个内隐类并继承自 FieldListenerAdapter 类,然后覆写 onBlur 方法使得一旦输入框失去焦点时触发此事件。在 onBlur 方法中实现调用此服务的过程。
清单 2. DemoFieldListenerAdapter 类
class DemoFieldListenerAdapter extends FieldListenerAdapter{ public void onBlur(Field field) { CalculateServiceAsync service = CalculateService.Util.getInstance(); service.getAddResult(textField1.getValueAsString(), textField2.getValueAsString(), new AsyncCallback(){ // 声明一个 //AsyncCallBack 实例 // 覆写 onFailure 方法 public void onFailure(Throwable caught) { caught.printStackTrace(); System.out.println("Communication failed"); } // 覆写 onSuccess 方法。 public void onSuccess(Object result) { String addResult = (String)result; resultLabel.setText(addResult); } } ); } } |
需要说明的是,Cypal Studio For GWT 插件在生成 CalculateService 时为我们将初始化 CalculateServiceAsync 实例的过程封装在一个静态内隐类中。
清单 3. 初始化 CalculateServiceAsync 实例
public static class Util { public static CalcuServiceAsync getInstance() { CalcuServiceAsync instance = (CalcuServiceAsync) GWT.create(CalcuService.class); ServiceDefTarget target = (ServiceDefTarget) instance; target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI); return instance; } } |
这样,我们每次调用时只需通过 CalculateService.Util.getInstance() 方法即可获得 CalcuServiceAsync 的实例。获得 CalcuServiceAsync 实例后,即可调用服务接口。这里的参数有三个,第一个是 textField1 的输入值,第二个是 textField2 的输入值,第三个是 AsyncCallback 对象。 AsyncCallback 对象采用匿名内隐类来创建并覆写 onFailure 和 onSuccess 方法。这两个方法会在服务器端执行完服务过程后在客户端执行。如果服务器端有错误发生,则 onFailure 方法被执行;如果没有错误,则 onSuccess 方法被执行。例子中,在 onSuccess 方法里将成功返回的结果放在 ResultLabel 中显示。
在 GWT 框架下实现同步通信
在 GWT 框架中,其所提供的通信方式,包括 HttpRequest, RequestBuilder, RPC 等都是异步的,要实现同步的通信,可以利用 Form 中提交 Action 的方式。即通过 HTML 中的 Document 所得到的 FormElement 来构建指向服务器端的一个链接(URL)。然后,向 Server 端提交此 Form 。这样,客户端会一直等待服务器端对此请求的相应。代码如下 ,
清单 4. Form 中提交 Action
String url= ” / ” ; FormElement formElement = Document.get().createFormElement(); formElement.setAction(url); formElement.setName("ThisActionName"); formElement.setMethod("post"); Document.get().appendChild(formElement); formElement.submit(); |