GWT - RPC 通信
基于 GWT 的应用程序通常由客户端模块和服务器端模块组成。客户端代码在浏览器中运行,服务器端代码在 Web 服务器中运行。客户端代码必须通过网络发出 HTTP 请求才能访问服务器端数据。
RPC(远程过程调用)是 GWT 使用的机制,客户端代码可以直接执行服务器端方法。
GWT RPC 基于 servlet。
GWT RPC 是异步的,客户端在通信过程中不会被阻止。
使用 GWT RPC,Java 对象可以直接在客户端和服务器之间发送(由 GWT 框架自动序列化)。
服务器端 servlet 称为服务。
从客户端代码调用服务器端 servlet 的方法的远程过程调用称为调用服务。
GWT RPC组件
以下是 GWT RPC 通信机制中使用的三个组件
- 在服务器上运行的远程服务(服务器端 servlet)。
- 用于调用该服务的客户端代码。
- 将在客户端和服务器之间传递的 Java 数据对象。
GWT 客户端和服务器都会自动序列化和反序列化数据,因此开发人员无需序列化/反序列化对象,并且数据对象可以通过 HTTP 传输。
下图显示了 RPC 架构。
要开始使用 RPC,我们需要遵循 GWT 约定。
RPC 通信工作流
步骤 1 - 创建可序列化的模型类
在客户端定义一个可序列化的 Java 模型对象。
public class Message implements Serializable { ... private String message; public Message(){}; public void setMessage(String message) { this.message = message; } ... }
步骤 2 - 创建服务接口
在客户端定义服务接口,扩展 RemoteService 并列出所有服务方法。
使用注释 @RemoteServiceRelativePath 将服务与远程 servlet 相对于模块基本 URL 的默认路径进行映射。
@RemoteServiceRelativePath("message") public interface MessageService extends RemoteService { Message getMessage(String input); }
步骤 3 - 创建异步服务接口
在客户端定义服务异步接口(与上述服务位于同一位置),该接口将在 GWT 客户端代码中使用。
public interface MessageServiceAsync { void getMessage(String input, AsyncCallback<Message> callback); }
步骤 4 - 创建服务实现 Servlet 类
在服务器端实现接口,该类应扩展 RemoteServiceServlet 类。
public class MessageServiceImpl extends RemoteServiceServlet implements MessageService{ ... public Message getMessage(String input) { String messageString = "Hello " + input + "!"; Message message = new Message(); message.setMessage(messageString); return message; } }
步骤 5 - 更新 Web.xml 以包含 Servlet 声明
编辑 Web 应用程序部署描述符 (web.xml) 以包含 MessageServiceImpl Servlet 声明。
<web-app> ... <servlet> <servlet-name>messageServiceImpl</servlet-name> <servlet-class>com.tutorialspoint.server.MessageServiceImpl </servlet-class> </servlet> <servlet-mapping> <servlet-name>messageServiceImpl</servlet-name> <url-pattern>/helloworld/message</url-pattern> </servlet-mapping> </web-app>
第 6 步 - 在应用程序代码中进行远程过程调用
创建服务代理类。
MessageServiceAsync messageService = GWT.create(MessageService.class);
创建 AsyncCallback Handler 来处理 RPC 回调,服务器通过该回调将消息返回给客户端
class MessageCallBack implements AsyncCallback<Message> { @Override public void onFailure(Throwable caught) { Window.alert("Unable to obtain server response: " + caught.getMessage()); } @Override public void onSuccess(Message result) { Window.alert(result.getMessage()); } }
当用户与 UI 交互时调用远程服务
public class HelloWorld implements EntryPoint { ... public void onModuleLoad() { ... buttonMessage.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { messageService.getMessage(txtName.getValue(), new MessageCallBack()); } }); ... } }
RPC 通信完整示例
此示例将带您完成一些简单的步骤,展示 GWT 中的 RPC 通信示例。按照以下步骤更新我们在 GWT - 创建应用程序 一章中创建的 GWT 应用程序 −
步骤 | 描述 |
---|---|
1 | 在 com.tutorialspoint 包下创建一个名为 HelloWorld 的项目,如 GWT - 创建应用程序 一章中所述。 |
2 | 修改 HelloWorld.gwt.xml、HelloWorld.css、HelloWorld.html 和 HelloWorld.java,如下所述。其余文件保持不变。 |
3 | 编译并运行应用程序以验证实现逻辑的结果。 |
以下是修改后的模块描述符src/com.tutorialspoint/HelloWorld.gwt.xml的内容。
<?xml version = "1.0" encoding = "UTF-8"?> <module rename-to = 'helloworld'> <!-- Inherit the core Web Toolkit stuff. --> <inherits name = 'com.google.gwt.user.User'/> <!-- Inherit the default GWT style sheet. --> <inherits name = 'com.google.gwt.user.theme.clean.Clean'/> <!-- Inherit the UiBinder module. --> <inherits name = "com.google.gwt.uibinder.UiBinder"/> <!-- Specify the app entry point class. --> <entry-point class = 'com.tutorialspoint.client.HelloWorld'/> <!-- Specify the paths for translatable code --> <source path = 'client'/> <source path = 'shared'/> </module>
以下是修改后的样式表文件war/HelloWorld.css的内容。
body { text-align: center; font-family: verdana, sans-serif; } h1 { font-size: 2em; font-weight: bold; color: #777777; margin: 40px 0px 70px; text-align: center; }
以下是修改后的 HTML 主机文件 war/HelloWorld.html 的内容。
<html> <head> <title>Hello World</title> <link rel = "stylesheet" href = "HelloWorld.css"/> <script language = "javascript" src = "helloworld/helloworld.nocache.js"> </script> </head> <body> <h1>RPC Communication Demonstration</h1> <div id = "gwtContainer"></div> </body> </html>
现在在 src/com.tutorialspoint/client 包中创建 Message.java 文件并将以下内容放入其中
package com.tutorialspoint.client; import java.io.Serializable; public class Message implements Serializable { private static final long serialVersionUID = 1L; private String message; public Message(){}; public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } }
现在在 src/com.tutorialspoint/client 包中创建 MessageService.java 文件,并将以下内容放入其中
package com.tutorialspoint.client; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; @RemoteServiceRelativePath("message") public interface MessageService extends RemoteService { Message getMessage(String input); }
现在在 src/com.tutorialspoint/client 包中创建 MessageServiceAsync.java 文件,并将以下内容放入其中
package com.tutorialspoint.client; import com.google.gwt.user.client.rpc.AsyncCallback; public interface MessageServiceAsync { void getMessage(String input, AsyncCallback<Message> callback); }
现在在 src/com.tutorialspoint/server 包中创建 MessageServiceImpl.java 文件,并将以下内容放入其中
package com.tutorialspoint.server; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.tutorialspoint.client.Message; import com.tutorialspoint.client.MessageService; public class MessageServiceImpl extends RemoteServiceServlet implements MessageService{ private static final long serialVersionUID = 1L; public Message getMessage(String input) { String messageString = "Hello " + input + "!"; Message message = new Message(); message.setMessage(messageString); return message; } }
更新已修改的 Web 应用程序部署描述符 war/WEB-INF/web.xml 的内容以包含 MessageServiceImpl Servlet 声明。
<?xml version = "1.0" encoding = "UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Default page to serve --> <welcome-file-list> <welcome-file>HelloWorld.html</welcome-file> </welcome-file-list> <servlet> <servlet-name>messageServiceImpl</servlet-name> <servlet-class>com.tutorialspoint.server.MessageServiceImpl </servlet-class> </servlet> <servlet-mapping> <servlet-name>messageServiceImpl</servlet-name> <url-pattern>/helloworld/message</url-pattern> </servlet-mapping> </web-app>
将 src/com.tutorialspoint/client 包中的 HelloWorld.java 内容替换为以下内容
package com.tutorialspoint.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DecoratorPanel; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class HelloWorld implements EntryPoint { private MessageServiceAsync messageService = GWT.create(MessageService.class); private class MessageCallBack implements AsyncCallback<Message> { @Override public void onFailure(Throwable caught) { /* 发生服务器端错误 */ Window.alert("Unable to obtain server response: " + caught.getMessage()); } @Override public void onSuccess(Message result) { /* 服务器返回结果,向用户显示消息 */ Window.alert(result.getMessage()); } } public void onModuleLoad() { /*create UI */ final TextBox txtName = new TextBox(); txtName.setWidth("200"); txtName.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { if(event.getNativeKeyCode() == KeyCodes.KEY_ENTER){ /* 远程调用服务器来获取消息 */ messageService.getMessage(txtName.getValue(), new MessageCallBack()); } } }); Label lblName = new Label("Enter your name: "); Button buttonMessage = new Button("Click Me!"); buttonMessage.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { /* 远程调用服务器来获取消息 */ messageService.getMessage(txtName.getValue(), new MessageCallBack()); } }); HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(lblName); hPanel.add(txtName); hPanel.setCellWidth(lblName, "130"); VerticalPanel vPanel = new VerticalPanel(); vPanel.setSpacing(10); vPanel.add(hPanel); vPanel.add(buttonMessage); vPanel.setCellHorizontalAlignment(buttonMessage, HasHorizontalAlignment.ALIGN_RIGHT); DecoratorPanel panel = new DecoratorPanel(); panel.add(vPanel); // 将小部件添加到根面板。 RootPanel.get("gwtContainer").add(panel); } }
完成所有更改后,让我们像在 GWT - 创建应用程序 一章中一样,在开发模式下编译并运行应用程序。如果您的应用程序一切正常,这将产生以下结果 −