Introduction to GWT – GWT简介

最后修改: 2018年 7月 4日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

GWT or Google Web Toolkit is a framework for building high-performance web applications in Java.

GWT或Google Web Toolkit是一个用Java构建高性能Web应用程序的框架

In this tutorial, we’re going to focus on and cover some of its key capabilities and functionality.

在本教程中,我们将重点介绍并涵盖它的一些关键能力和功能。

2. GWT SDK

2.GWT SDK

The SDK contains the Java API libraries, a compiler, and a development server.

SDK包含Java API库、一个编译器和一个开发服务器。

2.1. Java API

2.1. Java API

GWT API has classes for building user interfaces, making server calls, internationalization, performing unit tests. To learn more check the java documentation here.

GWT API拥有用于构建用户界面、进行服务器调用、国际化、执行单元测试的类。要了解更多信息,请查看java文档这里

2.2. Compiler

2.2.编译器

Simply put, GWT compiler is a source translator from Java code into the Javascript. The result of the compilation is a Javascript application.

简单地说,GWT编译器是一个从Java代码到Javascript的源码翻译器。编译的结果是一个Javascript应用程序。

The logic of its work includes trimming unused classes, methods, fields from the code and shortening Javascript names.

它的工作逻辑包括修剪代码中不使用的类、方法、字段和缩短Javascript名称。

Because of this advantage, we no longer need to include Ajax libraries in our Javascript project. Of course, it’s also possible to set hints while compiling the code.

由于这个优势,我们不再需要在Javascript项目中包含Ajax库。当然,也可以在编译代码的时候设置提示。

Here some useful GWTCompiler parameters:

这里有一些有用的GWTCompiler参数。

  • -logLevel – to set one of the ERROR, WARN, INFO, TRACE, DEBUG, SPAM, ALL logging levels
  • -workdir – compiler’s working directory
  • -gen – the directory to write the generated files
  • -out – the output files directory
  • -optimize – Sets the compiler optimization level from 0 to 9
  • -style – the script output style OBF, PRETTY or DETAILED
  • -module[s] – the name of the modules to compile

3. Setup

3.设置

The latest SDK is available over on download page. The rest of the setup is available at getting started page.

最新的SDK可以在下载页面上获得。其余的设置可在开始使用页面获得。

3.1. Maven

3.1.Maven

To set up the project with Maven, we need to add the following dependencies to pom.xml:

为了用Maven设置项目,我们需要在pom.xml中添加以下依赖项。

<dependency>
    <groupId>com.google.gwt</groupId>
    <artifactId>gwt-servlet</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.google.gwt</groupId>
    <artifactId>gwt-user</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.google.gwt</groupId>
    <artifactId>gwt-dev</artifactId>
    <scope>provided</scope>
</dependency>

The gwt-servlet library supports the server-side components for invoking a GWT-RPC endpoint. gwt-user contains the Java API which we’ll use to build our web application. gwt-dev has the code for compiler, deployment or hosting the application.

gwt-servlet库支持用于调用GWT-RPC端点的服务器端组件。gwt-user包含Java API,我们将用它来构建我们的Web应用程序gwt-dev有用于编译、部署或托管应用程序的代码。

To make sure that all the dependencies use the same version, we need to include the parent GWT dependency:

为了确保所有的依赖关系使用相同的版本,我们需要包括父代GWT依赖关系。

<dependency>
    <groupId>com.google.gwt</groupId>
    <artifactId>gwt</artifactId>
    <version>2.8.2</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

All the artifacts are available for the download on Maven Central.

所有工件都可在Maven Central上下载。

4. Application

4.应用

Let’s build a simple web application. It will send a message to the server and display the response.

让我们建立一个简单的网络应用程序。它将向服务器发送一条消息并显示响应。

In general, a GWT application consists of the server and the client parts. The client-side makes an HTTP request to connect with the server. To make it possible, GWT uses Remote Procedure Call or simply RPC mechanism.

一般来说,GWT应用程序由服务器和客户端部分组成。客户端发出HTTP请求,与服务器进行连接。为了实现这一点,GWT使用了远程程序调用或简单的RPC机制。

5. GWT and RPC

5.GWT和RPC

Getting back to our application, let’s see how the RPC communication is made. For that purpose, we create a service to receive a message from the server.

回到我们的应用程序,让我们看看RPC通信是如何进行的。为此,我们创建一个服务来接收来自服务器的消息。

Let’s first create an interface:

让我们首先创建一个接口。

@RemoteServiceRelativePath("greet")
public interface MessageService extends RemoteService {
    String sendMessage(String message) throws IllegalArgumentException;
}

The @RemoteServiceRelativePath annotation maps the service to the module’s /message relative URL. MessageService should extend from RemoteService marker interface to perform RPC communication.

@RemoteServiceRelativePath注解将服务映射到模块的/message相对URL。MessageService应该从RemoteService标记接口扩展,以执行RPC通信

The implementation of MessageService is on a server side:

MessageService的实现是在服务器端。

public class MessageServiceImpl extends RemoteServiceServlet 
  implements MessageService {

    public String sendMessage(String message) 
      throws IllegalArgumentException {
        if (message == null) {
            throw new IllegalArgumentException("message is null");
        }

        return "Hello, " + message + "!<br><br> Time received: " 
          + LocalDateTime.now();
    }
}

Our server class extends from the RemoteServiceServlet base servlet class. It will automatically deserialize incoming requests from the client and serialize outgoing responses from the server.

我们的服务器类扩展自 RemoteServiceServlet基础servlet类 它将自动反序列化来自客户端的传入请求并序列化来自服务器的传出响应

Now let’s see how we use it from the client side. The MessageService is only a definitive version of our service.

现在让我们看看我们如何从客户端使用它。MessageService只是我们服务的一个确定版本

To perform on the client side, we need to create the asynchronous version of our service:

为了在客户端执行,我们需要创建我们服务的异步版本。

public interface MessageServiceAsync {
    void sendMessage(String input, AsyncCallback<String> callback) 
      throws IllegalArgumentException;
}

Here we can see an extra argument in getMessage() method. We need async to notify the UI when the asynchronous call is complete. This way we prevent blocking the working UI thread.

这里我们可以看到getMessage()方法中的一个额外参数。我们需要async来在异步调用完成后通知UI。这样我们就可以防止阻塞工作中的UI线程。

6. Components and Their Lifecycle

6.组件及其生命周期

The SDK offers some UI elements and layouts for designing the graphical interfaces.

SDK提供了一些UI元素和布局,用于设计图形界面。

In general, all the UI components extends from the Widget class. Visually we have the element widgets that we can see, click or move on the screen:

一般来说,所有的UI组件都是从Widget类延伸出来的。从视觉上看,我们有可以在屏幕上看到、点击或移动的元素Widget。

  • component widgetsTextBox, TextArea, Button, RadioButton, CheckBox, etc…

and there are layout or panel widgets that compose and organize the screen:

并有布局或面板小工具来组成和组织屏幕。

  • panel widgets – HorizontalPanel, VerticalPanel, PopupPanel, TabPanel, etc…

Every time we add a widget or any other component to the code, GWT works hard to link the view element with the browser’s DOM.

每当我们在代码中添加一个widget或其他组件时,GWT都会努力将视图元素与浏览器的DOM联系起来

The constructor always initializes the root DOM element. When we attach a child widget to a parent component, it also causes binding at the DOM level. The entry point class contains the loading function which will be called first. This is where we define our widgets.

构造函数总是初始化根DOM元素。当我们将一个子部件附加到一个父部件上时,也会在DOM级别上引起绑定。入口点类包含将被首先调用的加载函数。这就是我们定义小部件的地方。

7. Entry Point

7.入境点

Let’s have a close look at the main entry point of the application:

让我们仔细看看这个应用程序的主要入口点。

public class Google_web_toolkit implements EntryPoint {

    private MessageServiceAsync messageServiceAsync = GWT.create(MessageService.class);

    public void onModuleLoad() {
        Button sendButton = new Button("Submit");
        TextBox nameField = new TextBox();
        nameField.setText("Hi there");

        sendButton.addStyleName("sendButton");

        RootPanel.get("nameFieldContainer").add(nameField);
        RootPanel.get("sendButtonContainer").add(sendButton);
    }
}

Every UI class implements the com.google.gwt.core.client.EntryPoint interface to mark it as a main entry for the module. It connects to the corresponding HTML document, where the java code executes.

每个UI类都实现了com.google.gwt.core.client.EntryPoint接口,以标记它为模块的主条目。它连接到相应的HTML文档,在那里执行java代码。

We can define GWT UI components and assign then to HTML tags with the same given ID. Entry point class overrides the entry point onModuleLoad() method, which is called automatically when loading the module.

我们可以定义GWT UI组件并将其分配给具有相同给定ID的HTML标签。入口点类重写了入口点onModuleLoad()方法,该方法在加载模块时被自动调用

Here we create the UI components, register event handlers, modify the browser DOM.

在这里我们创建UI组件,注册事件处理程序,修改浏览器DOM。

Now, let’s see how we create our remote server instance. For that purpose, we use GWT.create(MessageService.class) static method.

现在,让我们看看如何创建我们的远程服务器实例。为此,我们使用GWT.create(MessageService.class)静态方法。

It determines the requested type at compile-time. Seeing this method, GWT compiler generates many versions of code at compile time, only one of which needs to be loaded by a particular client during bootstrapping at runtime. This feature is widely used in RPC calls.

它在编译时确定请求的类型。看到这种方法,GWT编译器在编译时生成许多版本的代码,在运行时的引导过程中,只有一个版本需要被特定的客户端加载。这个功能在RPC调用中被广泛使用。

Here we also define the Button and TextBox widgets. To add attach them into the DOM tree we use the RootPanel class. It is the root panel and returns a singleton value to bind the widget elements:

这里我们也定义了ButtonTextBox部件。为了将它们附加到DOM树中,我们使用RootPanel。它是根面板,并返回一个单子值来绑定widget元素。

RootPanel.get("sendButtonContainer").add(sendButton);

First, it gets the root container marked with sendButtonContainer id. After we attach the sendButton to the container.

首先,它得到标有sendButtonContainer id的根容器。之后,我们将sendButton附加到该容器上。

8. HTML

8.HTML

Inside of the /webapp folder, we have Google_web_toolkit.html file.

/webapp文件夹中,我们有Google_web_toolkit.html文件。

We can mark the tag elements with the specific ids so the framework can bind them into Java objects:

我们可以用特定的id来标记标签元素,这样框架就可以将它们绑定到Java对象中

<body>
    <h1>Sample GWT Application</h1>
    <table align="center">
        <tr>
            <td colspan="2" style="font-weight:bold;">Please enter your message:</td>
        </tr>
        <tr>
            <td id="nameFieldContainer"></td>
            <td id="sendButtonContainer"></td>
        </tr>
    </table>
</body>

The <td> tags with nameFieldContainer and sendButtonContainer ids will be mapped to the Button and TextBox components.

带有nameFieldContainersendButtonContainer id的<td>标签将被映射到ButtonTextBox组件。

9. Main Module Descriptor

9.主要模块描述符

Let’s have a look at the typical configuration of the Google_web_toolkit.gwt.xml main module descriptor file:

让我们看一下Google_web_toolkit.gwt.xml主模块描述符文件的典型配置。

<module rename-to='google_web_toolkit'>
    <inherits name='com.google.gwt.user.User'/>
    <inherits name='com.google.gwt.user.theme.clean.Clean'/>
    <entry-point class='com.baeldung.client.Google_web_toolkit'/>
</module>

We make core GWT stuff accessible by including the com.google.gwt.user.User interface. Also, we can choose a default style sheet for our application. In this case, it is *.clean.Clean.

我们通过包括com.google.gwt.user.User接口,使GWT的核心东西可以被访问。此外,我们可以为我们的应用程序选择一个默认的样式表。在这种情况下,它是*.clean.Clean

The other available styling options are *.dark.Dark, *.standard.Standard, *.chrome.Chrome. The com.baeldung.client.Google_web_toolkit is also marked here with the <entry-point /> tag.

其他可用的造型选项是*.dark.Dark*.standard.Standard*.chrome.Chromecom.baeldung.client.Google_web_toolkit在这里也用<entry-point />标签来标记。

10. Adding Event Handlers

10.添加事件处理程序

To manage the mouse or keyboard typing events, GWT will use some handlers. They all extend from EventHandler interface and have a method with the event type argument.

为了管理鼠标或键盘输入的事件,GWT将使用一些处理程序。它们都是从EventHandler接口中延伸出来的,并且有一个带有事件类型参数的方法

In our example, we register the mouse click event handler.

在我们的例子中,我们注册了鼠标点击事件处理程序。

This will fire the onClick() method every time the button is pushed:

这将在每次按下按钮时启动onClick()方法。

closeButton.addClickHandler(new ClickHandler() {
    public void onClick(ClickEvent event) {
        vPanel.hide();
        sendButton.setEnabled(true);
        sendButton.setFocus(true);
    }
});

Here we can modify the widget state and behavior. In our example, we hide the vPanel and enable the sendButton.

在这里我们可以修改小组件的状态和行为。在我们的例子中,我们隐藏vPanel并启用sendButton

The other way is to define an inner class and implement the necessary interfaces:

另一种方法是定义一个内层类并实现必要的接口。

class MyHandler implements ClickHandler, KeyUpHandler {

    public void onClick(ClickEvent event) {
        // send message to the server
    }

    public void onKeyUp(KeyUpEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
            // send message to the server
        }
    }
}

In addition to ClickHandler, we also include here the KeyUpHandler interface to catch the keypress events. Here, inside of onKeyUp() method we can use the KeyUpEvent to check if the user pressed the Enter key.

除了ClickHandler之外,我们还包括KeyUpHandler接口来捕获按键事件。在这里,onKeyUp()方法中我们可以使用KeyUpEvent来检查用户是否按下了Enter键

And here how we use the MyHandler class to register both event handlers:

而这里我们如何使用MyHandler类来注册这两个事件处理程序。

MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);

11. Calling the Server

11.调用服务器

Now, we’re ready to send the message to the server. We’ll perform a remote procedure call with asynchronous sendMessage() method.

现在,我们准备将消息发送到服务器上。我们将用异步的sendMessage()方法执行一个远程过程调用。

The second parameter of the method is AsyncCallback<String> interface, where the String is the return type of the corresponding synchronous method:

方法的第二个参数是AsyncCallback<String>接口,其中的String是对应的同步方法的返回类型

messageServiceAsync.sendMessage(textToServer, new AsyncCallback<String>() {
    public void onFailure(Throwable caught) {
        serverResponseLabel.addStyleName("serverResponseLabelError");
        serverResponseLabel.setHTML("server error occurred");
        closeButton.setFocus(true);
    }

    public void onSuccess(String result) {
        serverResponseLabel.setHTML(result);
        vPanel.setVisible(true);
    }
});

As we can see, the receiver implements onSuccess(String result) and onFailure(Throwable) method for each response type.

我们可以看到,接收器为每种响应类型实现了onSuccess(String result) onFailure(Throwable) 方法。

Depending on response result, we either set an error message “server error occurred” or display the result value in the container.

根据响应结果,我们要么设置一个错误信息 “服务器发生错误”,要么在容器中显示结果值。

12. CSS Styling

12.CSS样式设计

When creating the project with the eclipse plugin, it will automatically generate the Google_web_toolkit.css file under the  /webapp directory and link it to the main HTML file.

当用eclipse插件创建项目时,它将自动在/webapp目录下生成Google_web_toolkit.css文件并将其链接到主HTML文件。

<link type="text/css" rel="stylesheet" href="Google_web_toolkit.css">

Of course, we can define custom styles for the specific UI components programmatically:

当然,我们可以通过编程为特定的UI组件定义自定义样式。

sendButton.addStyleName("sendButton");

Here we assign a CSS style with the class name sendButton to our sendButton component:

在这里,我们给我们的sendButton组件分配了一个CSS样式,类名为sendButton

.sendButton {
    display: block;
    font-size: 16pt;
}

13. Result

第13个结果

As a result, we have this simple web application:

结果是,我们有了这个简单的网络应用。

simpleApplication

Here we submit a “Hi there” message to the server and print the  “Hello, Hi there!” response on the screen.

在这里,我们向服务器提交一个 “你好 “的消息,并在屏幕上打印 “你好,你好!”的回应。

14. Conclusion

14.结论

In this quick article, we learned about the basics of GWT Framework. Afterward, we discussed the architecture, lifecycle, capabilities and different components of Its SDK.

在这篇快速文章中,我们了解了GWT框架的基础知识。之后,我们讨论了其SDK的架构、生命周期、功能和不同组件。

As a result, we learned how to create a simple web application.

结果,我们学会了如何创建一个简单的网络应用程序。

And, as always, the full source code of the tutorial is available over on GitHub.

而且,像往常一样,该教程的完整源代码可在GitHub上获得