用于 Java Web 应用程序的 Ajax 简介

本文档介绍了 Ajax,并展示了 NetBeans IDE 中的一些功能,这些功能使您在使用 Ajax 相关技术编程时更加快捷高效。在学习 Ajax 初级功能时,您将构建一个用于自动完成文本字段的简单应用程序。此处的内容改编自 Greg Murray 的文章,样例应用程序取自将 Ajax 与 Java 技术一起使用

Ajax 代表异步 JavaScript 和 XML。在本质上,Ajax 是一种 Web 应用程序用来处理用户与 Web 页间交互的有效方法,它不需要在每次用户交互时都进行页面刷新或重新加载完整页面。这样就能在使用浏览器时提供丰富的行为(与桌面应用程序或基于插件的 Web 应用程序的行为类似)。Ajax 交互在后台异步处理。同时,用户可以继续使用页面。Ajax 交互由 JavaScript 代码启动。Ajax 交互完成后,JavaScript 会更新页面的 HTML 源代码。这些更改会立即进行,不需要刷新页面。Ajax 交互可用于执行一些操作,如使用服务器端逻辑验证窗体条目(当用户输入窗体条目时)、检索服务器中的详细数据、动态更新页面上的数据,以及从页面提交部分窗体。

目录

此页上的内容适用于 NetBeans IDE 7.2、7.3、7.4 和 8.0

要学完本教程,您需要具备以下软件和资源。

软件或资源 要求的版本
NetBeans IDE,Java EE 包 7.3, 7.4, 8.0
Java 开发工具包 (JDK) 7 或 8
GlassFish Server

Tomcat 服务器
Open Source Edition 3.1.x 或 4.x

版本 7.x 或 8.x

注:

  • IDE 的 Java EE 安装使您能够有选择地安装 GlassFish Server 和 Apache Tomcat Servlet 容器。
  • 本教程假定您具有所使用的各种技术的应用知识(例如 HTML、CSS、JavaScript、Java 和 JSP)。它尝试以代码的形式提供功能的概述,但解释每行代码的含义。
  • 如果需要将项目与工作解决方案进行比较,可以下载样例应用程序

应用程序概述

设想一个用户可以在其中搜索作曲家信息的 Web 页。该页面包含一个字段,用户可以在其中输入作曲家的名字。在示例应用程序中,输入字段具有自动完成功能。换句话说,用户可以键入作曲家的部分名字,然后 Web 应用程序即可以列出其名字或姓氏以所输入字符开头的所有作曲家,尝试完成整个名字。自动完成功能使用户不必记住作曲家的完整名字,并且可以更加直观和直接的搜索热门信息。

浏览器中显示的样例应用程序

在搜索字段中实现自动完成功能可以使用 Ajax 来完成。Ajax 的工作原理是使用 XMLHttpRequest 对象在客户端和服务器之间异步传递请求和响应。下图说明了客户端和服务器之间进行通信的过程流。

Ajax 过程流图

该图中的过程流可以按以下步骤进行解释:

  1. 用户触发一个事件,例如,在键入姓名时释放一个按键。这会引发调用初始化 XMLHttpRequest 对象的 JavaScript 函数。
  2. XMLHttpRequest 对象将使用一个请求参数进行配置,该参数将包含触发该事件的组件 ID 以及用户所输入的任何值。然后,XMLHttpRequest 对象将向 Web 服务器发出异步请求。
  3. 在 Web 服务器上,诸如 servlet 或监听程序之类的对象会处理该请求。从数据存储检索数据,并且准备一个响应,其中包含 XML 文档形式的数据。
  4. 最后,XMLHttpRequest 对象使用回调函数接收 XML 数据并对其进行处理,然后更新 HTML DOM(文档对象模型)以显示包含新数据的页面。

本教程说明了如何根据上图所示的过程流构建自动完成方案。首先,为生成 XMLHttpRequest 对象所需的表示和功能创建客户端文件。然后,通过使用基于 Java 的技术创建数据存储和业务逻辑来设置服务器端。最后,返回客户端,并实现 callback() 和其他 JavaScript 功能以更新 HTML DOM。


客户端编程:第 1 部分

首先在 IDE 中新建一个新的 Web 应用程序项目。IDE 中包含多种项目类型的内置模板。

  1. 选择 "File"(文件)> "New Project"(新建项目)。在 "Categories"(类别)下,选择 "Java Web"。在 "Projects"(项目)下选择 "Web Application"(Web 应用程序),然后单击 "Next"(下一步)。
  2. 在 "Name and Location"(名称和位置)面板中,输入 MyAjaxApp 作为项目名称。使用 "Project Location"(项目位置)字段可以指定您计算机上项目的位置。将其他选项保留为默认设置,然后单击 "Next"(下一步)。
    新建 Web 应用程序向导 - "Name and Location"(名称和位置)面板
  3. 在 "Server and Settings"(服务器和设置)面板中,选择要将您的应用程序部署到的服务器。这里仅列出了已在 IDE 中注册的服务器。
    新建 Web 应用程序向导 - "Server Settings"(服务器设置)面板
  4. 接受其他默认设置,然后单击 "Finish"(完成)。项目在文件系统中生成,并在 IDE 中打开。

在创建基于 Java 的 Web 项目后,将自动构建用于编译该项目的 Ant 构建脚本,以便在 IDE 中注册的服务器上快速部署和运行该项目。

将生成一个默认的入口页,并在 IDE 的源代码编辑器中打开。根据目标服务器不同,入口页将为 index.jspindex.html

包含新创建的项目的 "Projects"(项目)窗口

在开始编码前,请立即尝试运行该应用程序以确保正确设置了 IDE、您的服务器和浏览器之间的配置。

  1. 在 "Projects"(项目)窗口中,右键单击该项目节点并选择 "Run"(运行)。

    编译应用程序,启动应用服务器,并在该服务器上部署和运行该应用程序。IDE 将打开默认浏览器并显示默认入口页。

使用 HTML 编辑器

显示 HTML 元素的 "Palette"(组件面板)

既然您确定已正确设置了您的环境,请首先将您的索引页转换成用户将查看的自动完成界面。

使用 IDE 的一个好处是:您所使用的编辑器通常可以为您提供代码完成功能,如果在编写代码时学会应用此功能,可以快速提高效率。IDE 的源代码编辑器通常适用您所使用的技术,因此,在使用 HTML 页时,按下代码完成组合键(Ctrl-空格键)将生成关于 HTML 标记和属性的建议。下面您还将了解到,IDE 编辑器也适用其他技术(如 CSS 和 JavaScript)。

您可以使用的第二个功能是 IDE 的 "Palette"(组件面板)。"Palette"(组件面板)为您编写代码所采用的技术中的常用元素提供了易于使用的模板。您只需单击某一项,然后将其拖至源代码编辑器所打开的文件中的某个位置。

您可以查看大图标(如此处显示),方法是右键单击组件面板,然后选择 "Show Big Icons"(显示大图标)。


  1. <title><h1> 标记的内容替换为:Auto-Completion using AJAX。索引页不需要服务器端脚本代码,因此可以安全地删除默认创建的任何遗留代码。现在,索引页应该如下显示。
    <!DOCTYPE html>
    
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>Auto-Completion using AJAX</title>
        </head>
        <body>
            <h1>Auto-Completion using AJAX</h1>
        </body>
    </html>
    
  2. 添加一些说明性文本以介绍文本字段的用途。您可以复制以下文本并将其粘贴在 <h1> 标记下方的某一位置:
    <p>This example shows how you can do real time auto-completion using Asynchronous
        JavaScript and XML (Ajax) interactions.</p>
    
    <p>In the form below enter a name. Possible names that will be completed are displayed
        below the form. For example, try typing in "Bach," "Mozart," or "Stravinsky,"
        then click on one of the selections to see composer details.</p>
    
  3. 向该页面中添加一个 HTML 窗体。可以利用 IDE "Palette"(组件面板)中列出的元素执行此操作。如果组件面板没有打开,请从主菜单中选择 "Window"(窗口)> "Palette"(组件面板)。然后,在 "HTML Forms"(HTML 窗体)下,单击某个窗体元素,并将其拖至该页面中您刚添加的 <p> 标记下的某一位置。此时将打开 "Insert Form"(插入窗体)对话框。指定以下内容:

    • 操作:autocomplete
    • "Method"(方法):GET
    • "Name"(名称):autofillform
    &quot;Insert form&quot;(插入窗体)对话框

    单击 "OK"(确定)。HTML <form> 标记已插入到包含您所指定的属性的页面中。(GET 在默认情况下应用,因此没有进行显式声明。)

  4. 向该页面添加一个 HTML 表格。在 "Palette"(组件面板)中的 "HTML" 类别下,单击某个表格元素,并将其拖至 <form> 标记之间的位置。"Insert Table"(插入表格)对话框即打开。指定以下内容:

    • "Rows"(行):2
    • "Columns"(列):2
    • "Border Size"(边框大小):0
    • "Cell Padding"(单元格边距):5
    &quot;Insert table&quot;(插入表格)对话框
  5. 在源代码编辑器中单击鼠标右键,然后选择 "Format"(格式化代码)。此操作将对代码进行整理。现在,您的窗体应该如下显示:
    <form name="autofillform" action="autocomplete">
      <table border="0" cellpadding="5">
        <thead>
          <tr>
            <th></th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td></td>
            <td></td>
          </tr>
          <tr>
            <td></td>
            <td></td>
          </tr>
        </tbody>
      </table>
    </form>
    
  6. 将以下文本键入到此表格第一行的第一列中(更改的内容以粗体显示):
    <td><strong>Composer Name:</strong></td>
  7. 在第一行的第二列中,手动键入以下代码,而不是从组件面板中拖动 "Text Input"(文本输入)字段(更改的内容以粗体显示):
    <td>
        <input type="text"
            size="40"
            id="complete-field"
            onkeyup="doCompletion();">
    </td>
    
    在键入时,请尝试使用 IDE 内置的代码完成支持。例如,键入 <i,然后按 Ctrl-空格组合键。建议的选项列表会显示在光标下方,并且选中元素的说明会显示在上方的框中。事实上,在源代码编辑器中编写代码时,可以随时按 Ctrl-空格键调出可能的选项。而且,如果只有一个可用的选项,按 Ctrl-空格键将自动完成元素名称。
    源代码编辑器中显示的代码完成

您在上文中键入的 onkeyup 属性指向名为 doCompletion() 的 JavaScript 函数。每次在窗体文本字段中按下一个键时,此函数都会被调用,并映射到以上 Ajax 流程图中所描述的 JavaScript 调用。

使用 JavaScript 编辑器

IDE 的 JavaScript 编辑器提供了许多高级编辑功能,如智能代码完成、语义突出显示、即时重命名和重构功能等。

当您使用其他技术(即 HTML、RHTML、JSP、PHP)在 .js 文件以及 <script> 标记中编码时,将自动提供 JavaScript 代码完成功能。当编辑 JavaScript 代码时,IDE 可以为您提供一些提示。通过选择 "Tools"(工具)> "Options"(选项)(在 Mac 上为 "NetBeans" > "Preferences"(首选项))打开 "Options"(选项)窗口,并在 "Editor"(编辑器)类别的 "Hints"(提示)标签中选择 "JavaScript" 语言,可以指定 JavaScript 提示选项。此外,您还可以在 "Options"(选项)窗口中的 "Code Templates"(代码模板)标签中添加您自己的 JavaScript 代码模板。

&quot;Options&quot;(选项)窗口中的 JavaScript 提示选项

将 JavaScript 文件添加到此应用程序,然后开始实现 doCompletion()

  1. 在 "Projects"(项目)窗口中,右键单击 "Web Pages"(Web 页)节点,然后选择 "New"(新建)> "JavaScript file"(JavaScript 文件)。(如果 "JavaScript file"(JavaScript 文件)未列出,请选择 "Other"(其他)。然后从新建文件向导的 "Web" 类别中选择 "JavaScript file"(JavaScript 文件)。)
  2. 将文件命名为 javascript,然后单击 "Finish"(完成)。新的 JavaScript 文件将显示在 "Projects"(项目)窗口的 "Web Pages"(Web 页)文件夹中。
  3. 将以下代码键入 javascript.js
    var req;
    var isIE;
    
    function init() {
        completeField = document.getElementById("complete-field");
    }
    
    function doCompletion() {
            var url = "autocomplete?action=complete&id=" + escape(completeField.value);
            req = initRequest();
            req.open("GET", url, true);
            req.onreadystatechange = callback;
            req.send(null);
    }
    
    function initRequest() {
        if (window.XMLHttpRequest) {
            if (navigator.userAgent.indexOf('MSIE') != -1) {
                isIE = true;
            }
            return new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            isIE = true;
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    }

    以上代码将对 Firefox 3 以及 Internet Explorer 版本 6 和 7 执行简单的浏览器兼容性检查。如果希望包含更多强健的代码以处理兼容性问题,建议您使用 http://www.quirksmode.org 中的浏览器检测脚本

  4. 切换回索引页,然后在 <head> 标记之间添加对 JavaScript 文件的引用。
    <script type="text/javascript" src="javascript.js"></script>

    您可以按 Ctrl-Tab 组合键在源代码编辑器中打开的页面之间快速切换。

  5. 在开始标记 <body> 中,插入对 init() 的调用。
    <body onload="init()">
    这可以确保每次加载页面时,都会调用 init()

doCompletion() 的作用是:

  • 创建一个 URL,其中包含可由服务器端使用的数据,
  • 初始化 XMLHttpRequest 对象,并
  • 提示 XMLHttpRequest 对象向服务器发送一个异步请求。

XMLHttpRequest 对象是 Ajax 的核心,并已经成为通过 HTTP 异步传递 XML 数据的实际标准。异步交互意味着在发送请求之后浏览器可以继续处理页面中的事件。数据在后台进行传递,并且可以自动加载到页面中,无需进行页面刷新。

请注意,XMLHttpRequest 对象实际上是由 initRequest()(由 doCompletion() 调用)创建的。此函数用于检查浏览器是否可以识别 XMLHttpRequest;如果可以,即创建 XMLHttpRequest 对象。否则,它将对 ActiveXObject(相当于 Internet Explorer 6 的 XMLHttpRequest)执行检查,并创建 ActiveXObject(如果被识别)。

当您创建 XMLHttpRequest 对象时会指定三个参数:URL、HTTP 方法(GETPOST),以及此交互是否为异步交互。以上示例中的参数有:

  • URL,autocomplete 和用户输入 complete-field 的文本:
    var url = "autocomplete?action=complete&id=" + escape(completeField.value);
  • GET,表示 HTTP 交互使用 GET 方法,以及
  • true,表示此交互是异步交互:
    req.open("GET", url, true);

如果此交互设为异步交互,则必须指定回调函数。此交互的回调函数是使用以下语句进行设置的:

req.onreadystatechange = callback;

并且稍后必须定义 callback() 函数。HTTP 交互在调用 XMLHttpRequest.send() 时开始。在以上流程图中,此操作映射到发送给 Web 服务器的 HTTP 请求。


服务器端编程

IDE 对服务器端 Web 编程提供全面支持。它不但包括对许多常用编程和脚本语言的基本编辑器支持,还包括 Web 服务(例如 SOAP、REST、SaaS)及面向 MVC 的框架(例如 JSF、Spring 和 Struts)。从 NetBeans 插件门户可以获取由 Ajax 驱动的框架的若干 NetBeans 插件,其中包括 GWTStruts2

应用程序的业务逻辑通过以下方式处理请求:从数据存储中检索数据,然后准备和发送响应。这里使用一个 servlet 实现该任务。在您对 servlet 进行编码之前,请设置数据存储和 servlet 访问数据所需的功能。

创建数据存储

对于这种简单的应用程序,您可以创建一个名为 ComposerData 的类,在其中使用 HashMap 保留作曲家的数据。HashMap 允许以键值对的形式存储链接项目对。还要创建一个 Composer 类,使 servlet 能够从 HashMap 中的条目检索数据。

  1. 在“项目”窗口中右键单击项目节点,然后选择“新建”>“Java 类”。
  2. 将该类命名为 ComposerData,并在“包”字段中输入 com.ajax。这将创建新包以包含该类,以及以后要创建的其他类。
  3. 单击“完成”。该类随即创建,并在源代码编辑器中打开。
  4. 在源代码编辑器中,粘贴以下代码:
    package com.ajax;
    
    import java.util.HashMap;
    
    /**
     *
     * @author nbuser
     */
    public class ComposerData {
    
        private HashMap composers = new HashMap();
    
        public HashMap getComposers() {
            return composers;
        }
    
        public ComposerData() {
    
            composers.put("1", new Composer("1", "Johann Sebastian", "Bach", "Baroque"));
            composers.put("2", new Composer("2", "Arcangelo", "Corelli", "Baroque"));
            composers.put("3", new Composer("3", "George Frideric", "Handel", "Baroque"));
            composers.put("4", new Composer("4", "Henry", "Purcell", "Baroque"));
            composers.put("5", new Composer("5", "Jean-Philippe", "Rameau", "Baroque"));
            composers.put("6", new Composer("6", "Domenico", "Scarlatti", "Baroque"));
            composers.put("7", new Composer("7", "Antonio", "Vivaldi", "Baroque"));
    
            composers.put("8", new Composer("8", "Ludwig van", "Beethoven", "Classical"));
            composers.put("9", new Composer("9", "Johannes", "Brahms", "Classical"));
            composers.put("10", new Composer("10", "Francesco", "Cavalli", "Classical"));
            composers.put("11", new Composer("11", "Fryderyk Franciszek", "Chopin", "Classical"));
            composers.put("12", new Composer("12", "Antonin", "Dvorak", "Classical"));
            composers.put("13", new Composer("13", "Franz Joseph", "Haydn", "Classical"));
            composers.put("14", new Composer("14", "Gustav", "Mahler", "Classical"));
            composers.put("15", new Composer("15", "Wolfgang Amadeus", "Mozart", "Classical"));
            composers.put("16", new Composer("16", "Johann", "Pachelbel", "Classical"));
            composers.put("17", new Composer("17", "Gioachino", "Rossini", "Classical"));
            composers.put("18", new Composer("18", "Dmitry", "Shostakovich", "Classical"));
            composers.put("19", new Composer("19", "Richard", "Wagner", "Classical"));
    
            composers.put("20", new Composer("20", "Louis-Hector", "Berlioz", "Romantic"));
            composers.put("21", new Composer("21", "Georges", "Bizet", "Romantic"));
            composers.put("22", new Composer("22", "Cesar", "Cui", "Romantic"));
            composers.put("23", new Composer("23", "Claude", "Debussy", "Romantic"));
            composers.put("24", new Composer("24", "Edward", "Elgar", "Romantic"));
            composers.put("25", new Composer("25", "Gabriel", "Faure", "Romantic"));
            composers.put("26", new Composer("26", "Cesar", "Franck", "Romantic"));
            composers.put("27", new Composer("27", "Edvard", "Grieg", "Romantic"));
            composers.put("28", new Composer("28", "Nikolay", "Rimsky-Korsakov", "Romantic"));
            composers.put("29", new Composer("29", "Franz Joseph", "Liszt", "Romantic"));
    
            composers.put("30", new Composer("30", "Felix", "Mendelssohn", "Romantic"));
            composers.put("31", new Composer("31", "Giacomo", "Puccini", "Romantic"));
            composers.put("32", new Composer("32", "Sergei", "Rachmaninoff", "Romantic"));
            composers.put("33", new Composer("33", "Camille", "Saint-Saens", "Romantic"));
            composers.put("34", new Composer("34", "Franz", "Schubert", "Romantic"));
            composers.put("35", new Composer("35", "Robert", "Schumann", "Romantic"));
            composers.put("36", new Composer("36", "Jean", "Sibelius", "Romantic"));
            composers.put("37", new Composer("37", "Bedrich", "Smetana", "Romantic"));
            composers.put("38", new Composer("38", "Richard", "Strauss", "Romantic"));
            composers.put("39", new Composer("39", "Pyotr Il'yich", "Tchaikovsky", "Romantic"));
            composers.put("40", new Composer("40", "Guiseppe", "Verdi", "Romantic"));
    
            composers.put("41", new Composer("41", "Bela", "Bartok", "Post-Romantic"));
            composers.put("42", new Composer("42", "Leonard", "Bernstein", "Post-Romantic"));
            composers.put("43", new Composer("43", "Benjamin", "Britten", "Post-Romantic"));
            composers.put("44", new Composer("44", "John", "Cage", "Post-Romantic"));
            composers.put("45", new Composer("45", "Aaron", "Copland", "Post-Romantic"));
            composers.put("46", new Composer("46", "George", "Gershwin", "Post-Romantic"));
            composers.put("47", new Composer("47", "Sergey", "Prokofiev", "Post-Romantic"));
            composers.put("48", new Composer("48", "Maurice", "Ravel", "Post-Romantic"));
            composers.put("49", new Composer("49", "Igor", "Stravinsky", "Post-Romantic"));
            composers.put("50", new Composer("50", "Carl", "Orff", "Post-Romantic"));
    
        }
    }

您会注意到由于找不到 Composer 类,因此会在编辑器左旁注中显示一条警告。请执行以下步骤以便创建 Composer 类。

  1. 在“项目”窗口中右键单击项目节点,然后选择“新建”>“Java 类”。
  2. 将该类命名为 Composer,并从“包”字段的下拉列表中选择 com.ajax。单击“完成”。

    单击“完成”,此时 IDE 将创建此类并在源代码编辑器中打开该文件。

  3. 在源代码编辑器中,粘贴以下代码:
    package com.ajax;
    
    public class Composer {
    
        private String id;
        private String firstName;
        private String lastName;
        private String category;
    
        public Composer (String id, String firstName, String lastName, String category) {
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
            this.category = category;
        }
    
        public String getCategory() {
            return category;
        }
    
        public String getId() {
            return id;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    }

在创建 Composer 类之后,如果在编辑器中查看 ComposerData 类,您可以看到编辑器中不再显示警告标注。如果您仍会在 ComposerData 中看到警告标注,则可以尝试通过添加任何缺少的 import 语句来解决错误。

创建 Servlet

创建一个 servlet 以处理由传入请求接收的 autocomplete URL。

  1. 在 "Projects"(项目)窗口中右键单击项目节点,然后选择 "New"(新建)> "Servlet" 以打开新建 Servlet 向导。(如果在默认情况下弹出式菜单中未显示 "Servlet",请选择 "Other"(其他),并从 "Web" 类别中选择 "Servlet"。)
  2. 将该 servlet 命名为 AutoCompleteServlet,并从 "Package"(包)字段的下拉列表中选择 com.ajax。单击 "Next"(下一步)。
    新建 Servlet 向导,已完成 &quot;Name and Location&quot;(名称和位置)窗格
  3. 在 "Configure Servlet Deployment"(配置 Servlet 部署)面板中,将 URL 模式更改为 /autocomplete,使之与以前在 XMLHttpRequest 对象中设置的 URL 匹配。
    新建 Servlet 向导,已完成 &quot;Configure Servlet Deployment&quot;(配置 Servlet 部署)面板

    此面板可以省去手动向部署描述符添加这些详细信息所需的步骤。

  4. 也可以选择 "Add servlet information to deployment descriptor"(将 Servlet 信息添加到部署描述符)。这样,您的项目将与下载的样例相同。在使用 IDE 的高级版本时,默认情况下用 @WebServlet 标注而不是部署描述符来注册 Servlet。如果您使用 @WebServlet 标注而不是部署描述符,该项目仍将工作。
  5. 单击 "Finish"(完成)。该 servlet 随即创建,并在源代码编辑器中打开。

您需要覆盖的方法只有 doGet()(该方法用于定义 servlet 处理 autocomplete GET 请求的方式)以及 init()(该方法需要启动一个 ServletContext,以便在应用程序提供服务时 servlet 可以访问该应用程序中的其他类)。

使用 IDE 的 "Insert Code"(插入代码)弹出式菜单可以覆盖超类的方法。通过执行下列步骤来实现 init()

  1. 将光标放在源代码编辑器中的 AutoCompleteServlet 类声明下。按 Alt-Insert 组合键(在 Mac 上按 Ctrl-I 组合键)打开 "Generate Code"(生成代码)弹出式菜单。
    源代码编辑器中显示的 &quot;Insert Code&quot;(插入代码)弹出式菜单
  2. 选择 "Override Method"(覆盖方法)。在显示的对话框中,将显示 AutoCompleteServlet 继承的所有类。展开 GenericServlet 节点并选择 init(Servlet Config config)
    列出继承的类的 &quot;Override&quot;(覆盖)对话框
  3. 单击 "OK"(确定)。init() 方法将添加到源代码编辑器中。
  4. ServletContext 对象添加一个变量并修改 init()(更改的内容以粗体显示):
    private ServletContext context;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.context = config.getServletContext();
    }
  5. ServletContext 添加一个导入语句。通过单击源代码编辑器左旁注中显示的灯泡图标可以执行此操作
    显示在源代码编辑器的左旁注中的导入提示

doGet() 方法需要解析请求的 URL,从数据存储中检索数据,并准备 XML 格式的响应。注意,方法声明是在创建类时生成的。要查看它,您可能需要通过单击左旁注中的 "expand"(展开)图标 (&quot;expand&quot;(展开)图标) 来展开 HttpServlet 方法。

  1. AutocompleteServlet 类声明下添加以下变量声明。
    private ComposerData compData = new ComposerData();
    private HashMap composers = compData.getComposers();
    这将创建所有作曲家数据的 HashMap,然后由 doGet() 使用。
  2. 向下滚动到 doGet() 并按如下方式实现该方法:
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
        String action = request.getParameter("action");
        String targetId = request.getParameter("id");
        StringBuffer sb = new StringBuffer();
    
        if (targetId != null) {
            targetId = targetId.trim().toLowerCase();
        } else {
            context.getRequestDispatcher("/error.jsp").forward(request, response);
        }
    
        boolean namesAdded = false;
        if (action.equals("complete")) {
    
            // check if user sent empty string
            if (!targetId.equals("")) {
    
                Iterator it = composers.keySet().iterator();
    
                while (it.hasNext()) {
                    String id = (String) it.next();
                    Composer composer = (Composer) composers.get(id);
    
                    if ( // targetId matches first name
                         composer.getFirstName().toLowerCase().startsWith(targetId) ||
                         // targetId matches last name
                         composer.getLastName().toLowerCase().startsWith(targetId) ||
                         // targetId matches full name
                         composer.getFirstName().toLowerCase().concat(" ")
                            .concat(composer.getLastName().toLowerCase()).startsWith(targetId)) {
    
                        sb.append("<composer>");
                        sb.append("<id>" + composer.getId() + "</id>");
                        sb.append("<firstName>" + composer.getFirstName() + "</firstName>");
                        sb.append("<lastName>" + composer.getLastName() + "</lastName>");
                        sb.append("</composer>");
                        namesAdded = true;
                    }
                }
            }
    
            if (namesAdded) {
                response.setContentType("text/xml");
                response.setHeader("Cache-Control", "no-cache");
                response.getWriter().write("<composers>" + sb.toString() + "</composers>");
            } else {
                //nothing to show
                response.setStatus(HttpServletResponse.SC_NO_CONTENT);
            }
        }
        if (action.equals("lookup")) {
    
            // put the target composer in the request scope to display 
            if ((targetId != null) && composers.containsKey(targetId.trim())) {
                request.setAttribute("composer", composers.get(targetId));
                context.getRequestDispatcher("/composer.jsp").forward(request, response);
            }
        }
    }

正如您在 servlet 中看到的,编写用于进行 Ajax 处理的服务器端的代码时并没有什么真正的新内容要了解。如果希望交换 XML 文档,则需要将响应内容类型设置为 text/xml。通过 Ajax,您还可以交换纯文本,甚至可以交换可在客户端由回调函数计算或执行的 JavaScript 片段。还请注意,有些浏览器可能会缓存结果,因此可能需要将 Cache-Control HTTP 头信息设置为 no-cache

在本例中,servlet 生成一个包含所有作曲家的 XML 文档,其名字和姓氏以用户键入的字符开头。本文档会映射到以上流程图中所描述的 XML 数据。以下是返回到 XMLHttpRequest 对象的 XML 文档的示例:

<composers>
    <composer>
        <id>12</id>
        <firstName>Antonin</firstName>
        <lastName>Dvorak</lastName>
    </composer>
    <composer>
        <id>45</id>
        <firstName>Aaron</firstName>
        <lastName>Copland</lastName>
    </composer>
    <composer>
        <id>7</id>
        <firstName>Antonio</firstName>
        <lastName>Vivaldi</lastName>
    </composer>
    <composer>
        <id>2</id>
        <firstName>Arcangelo</firstName>
        <lastName>Corelli</lastName>
    </composer>
</composers>

在完成应用程序之后,可以使用 IDE 的 HTTP 监视器查看返回的 XML 数据。

客户端编程:第 2 部分

现在,您必须定义回调函数,以处理服务器的响应,同时添加一些必要的功能,以反映用户所查看页面中的更改。这需要修改 HTML DOM。您需要创建 JSP 页以便显示成功请求的结果或失败请求的错误消息。然后,您可以创建简单的样式表以便演示。

添加回调功能

XMLHttpRequest 对象的 readyState 属性发生更改时,回调函数会在 HTTP 交互过程中的某个特定点被异步调用。在您要构建的应用程序中,回调函数是 callback()。您可以回想一下,在 doCompletion() 中,callback 设置为某个函数的 XMLHttpRequest.onreadystatechange 属性。现在,按以下步骤实现回调函数。

  1. 在源代码编辑器中打开 javascript.js,然后键入以下代码。
    function callback() {
        if (req.readyState == 4) {
            if (req.status == 200) {
                parseMessages(req.responseXML);
            }
        }
    }

readyState 为 "4" 表示 HTTP 交互完成。XMLHttpRequest.readState 的 API 表示可以设置 5 个值。它们是:

readyState 对象状态定义
0 未初始化
1 正在加载
2 已加载
3 交互中
4 完成

请注意,仅当 XMLHttpRequest.readyState 为 "4" 并且 status(请求的 HTTP 状态代码定义)为 "200"(表示成功)时,才会调用 parseMessages() 函数。您将在下面的更新 HTML DOM 部分中定义 parseMessages()

更新 HTML DOM

parseMessages() 函数用于处理传入的 XML 数据。为了实现此功能,它需要依靠若干附属的函数,如 appendComposer()getElementY()clearTable()。您还必须向此索引页引入新的元素(如用作自动完成框的另一个 HTML 表格),以及元素的 ID,以便它们可以在 javascript.js 中引用。最后,创建对应于索引页中元素 ID 的新变量,并且在之前实现的 init() 函数中对其进行初始化,然后添加每次加载索引页时所需要的一些功能。

注:您在以下步骤中创建的函数和元素之间存在相互依赖关系。建议您完成此部分,然后在代码全部完成之后检查此代码。

  1. 在源代码编辑器中打开索引页,并在刚才创建的 HTML 表格的第二行中键入以下代码。
    <tr>
        <td id="auto-row" colspan="2">
            <table id="complete-table" />
        </td>
    </tr>
    此表格的第二行包含其他 HTML 表格。此表格表示将用于填写作曲家名字的自动完成框。
  2. 在源代码编辑器中打开 javascript.js,并在文件顶部添加以下三个变量。
    var completeField;
    var completeTable;
    var autoRow;
  3. 将以下行(以粗体显示)添加到 init() 函数中。
    function init() {
        completeField = document.getElementById("complete-field");
        completeTable = document.getElementById("complete-table");
        autoRow = document.getElementById("auto-row");
        completeTable.style.top = getElementY(autoRow) + "px";
    }
    init() 的一个作用是使修改索引页 DOM 的其他函数可以访问索引页内的元素。
  4. appendComposer() 添加到 javascript.js
    function appendComposer(firstName,lastName,composerId) {
    
        var row;
        var cell;
        var linkElement;
    
        if (isIE) {
            completeTable.style.display = 'block';
            row = completeTable.insertRow(completeTable.rows.length);
            cell = row.insertCell(0);
        } else {
            completeTable.style.display = 'table';
            row = document.createElement("tr");
            cell = document.createElement("td");
            row.appendChild(cell);
            completeTable.appendChild(row);
        }
    
        cell.className = "popupCell";
    
        linkElement = document.createElement("a");
        linkElement.className = "popupItem";
        linkElement.setAttribute("href", "autocomplete?action=lookup&id=" + composerId);
        linkElement.appendChild(document.createTextNode(firstName + " " + lastName));
        cell.appendChild(linkElement);
    }
    此函数创建了一个新的表行,并用其所含的三个参数传递的数据将指向作曲家的链接插入此表行中,然后将此行插入索引页的 complete-table 元素中。
  5. getElementY() 添加到 javascript.js
    function getElementY(element){
    
        var targetTop = 0;
    
        if (element.offsetParent) {
            while (element.offsetParent) {
                targetTop += element.offsetTop;
                element = element.offsetParent;
            }
        } else if (element.y) {
            targetTop += element.y;
        }
        return targetTop;
    }
    此函数用于查找父元素的垂直位置。这是必要的,因为此元素的实际位置(如果显示)通常根据浏览器的类型和版本而定。请注意,如果 complete-table 元素显示包含作曲家名字,则会移动到其所在表格中的右下角。正确的高度定位由 getElementY() 确定。

    注:请在 http://www.quirksmode.org/ 上查看 offset说明

  6. clearTable() 添加到 javascript.js
    function clearTable() {
        if (completeTable.getElementsByTagName("tr").length > 0) {
            completeTable.style.display = 'none';
            for (loop = completeTable.childNodes.length -1; loop >= 0 ; loop--) {
                completeTable.removeChild(completeTable.childNodes[loop]);
            }
        }
    }
    此函数用于将 complete-table 元素的显示方式设置为“无”(也就是使其不可见),并删除所有已创建的现有作曲家名字条目。
  7. 修改 callback() 函数以便每次从服务器接收到新数据时都调用 clearTable()。因此,在用新条目填充自动完成框之前其中已存在的任何作曲家条目均会删除。
    function callback() {
    
        clearTable();
    
        if (req.readyState == 4) {
            if (req.status == 200) {
                parseMessages(req.responseXML);
            }
        }
    }
  8. parseMessages() 添加到 javascript.js
    function parseMessages(responseXML) {
    
        // no matches returned
        if (responseXML == null) {
            return false;
        } else {
    
            var composers = responseXML.getElementsByTagName("composers")[0];
    
            if (composers.childNodes.length > 0) {
                completeTable.setAttribute("bordercolor", "black");
                completeTable.setAttribute("border", "1");
    
                for (loop = 0; loop < composers.childNodes.length; loop++) {
                    var composer = composers.childNodes[loop];
                    var firstName = composer.getElementsByTagName("firstName")[0];
                    var lastName = composer.getElementsByTagName("lastName")[0];
                    var composerId = composer.getElementsByTagName("id")[0];
                    appendComposer(firstName.childNodes[0].nodeValue,
                        lastName.childNodes[0].nodeValue,
                        composerId.childNodes[0].nodeValue);
                }
            }
        }
    }

parseMessages() 函数作为参数接收由 AutoComplete servlet 返回的 XML 文档的对象表示。此函数以编程方式遍历 XML 文档,提取每个条目的 firstNamelastNameid,然后将数据传递到 appendComposer()。这将导致动态更新 complete-table 元素的内容。例如,已生成并插入 complete-table 中的条目如下所示:

<tr>
    <td class="popupCell">
        <a class="popupItem" href="autocomplete?action=lookup&id=12">Antonin Dvorak</a>
    </td>
</tr>

complete-table 元素的动态更新是使用 Ajax 进行通信的过程中所产生的通信过程流的最后一步。此更新会映射到正在发送给以上流程图中表示的 HTML 和 CSS 数据。

显示结果

要显示结果,您需要一个名为 composers.jsp 的 JSP 文件。在查找操作期间会从 AutoCompleteServlet 中调用此页。您还需要一个 error.jsp 文件,如果找不到编写器,则从 AutoCompleteServlet 中调用此文件。

显示结果和错误:

  1. 在“项目”窗口中,右键单击应用程序的“Web 页”文件夹并选择“新建”> "JSP"。此时将打开新建 JSP 向导。
  2. 在“文件名”字段中,键入 composer。在“创建的文件”字段中,您会看到一个以 /web/composer.jsp 结尾的路径。
  3. 单击“完成”。composer.jsp 文件在编辑器中打开。该文件的节点将显示在“项目”窗口的“Web 页”文件夹中。
  4. composer.jsp 中的占位符代码替换为以下代码:
    <html>
      <head>
        <title>Composer Information</title>
    
        <link rel="stylesheet" type="text/css" href="stylesheet.css">
      </head>
      <body>
    
        <table>
          <tr>
            <th colspan="2">Composer Information</th>
          </tr>
          <tr>
            <td>First Name: </td>
            <td>${requestScope.composer.firstName}</td>
          </tr>
          <tr>
            <td>Last Name: </td>
            <td>${requestScope.composer.lastName}</td>
          </tr>
          <tr>
            <td>ID: </td>
            <td>${requestScope.composer.id}</td>
          </tr>
          <tr>
            <td>Category: </td>
            <td>${requestScope.composer.category}</td>
          </tr>      
        </table>
    
        <p>Go back to <a href="index.html" class="link">application home</a>.</p>
      </body>
    </html>

    注:如果索引页为 index.jsp,则需要更改链接以返回到该索引页。

  5. 在该项目的“Web 页”文件夹下创建另一个 JSP 文件。将该文件命名为 error.jsp
  6. error.jsp 中的占位符代码替换为以下代码:
    <!DOCTYPE html>
    
    <html>
        <head>
            <link rel="stylesheet" type="text/css" href="stylesheet.css">      
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>Seach Error</title>
        </head>
        <body>
            <h2>Seach Error</h2>
            
            <p>An error occurred while performing the search. Please try again.</p>
            
            <p>Go back to <a href="index.html" class="link">application home</a>.</p>
        </body>
    </html>

    注:如果索引页为 index.jsp,则需要更改链接以返回到该索引页。

附加样式表

在此阶段,您已完成了实现此应用程序功能所需的所有代码。要查看您的努力成果,请立即尝试运行此应用程序。

  1. 在 "Projects"(项目)窗口中,右键单击该项目节点并选择 "Run"(运行)。此项目会重新编译,并部署到目标服务器。此时您的浏览器会打开,并可以查看索引页。
    浏览器中显示的不带样式表的应用程序

要向您的应用程序添加一个样式表,只需创建 .css 文件,并从您的演示页面链接到此文件。当您使用 .css 文件时,IDE 会为您提供代码完成支持,以及以下有助于生成和编辑样式表规则的窗口。

  • "CSS Styles"(CSS 样式)窗口。"CSS Styles"(CSS 样式)窗口用于编辑 CSS 文件中的 HTML 元素和选择器的规则声明。
  • "Create CSS Rules"(创建 CSS 规则)对话框。"Create CSS Rule"(创建 CSS 规则)对话框用于在 CSS 样式表中创建新规则。
  • "Add CSS Property"(添加 CSS 属性)对话框。"Add CSS Property"(添加 CSS 属性)对话框用于通过添加属性和值来为样式表中 CSS 规则添加声明。

要向您的应用程序添加一个样式表,请执行以下步骤。

  1. 在 "Projects"(项目)窗口中,右键单击 "Web Pages"(Web 页)节点,然后选择 "New"(新建)> "Cascading Style Sheet"(级联样式表)(如果 "Cascading Style Sheet"(级联样式表)未列出,则选择 "Other"(其他)。然后从新建文件向导的 "Web" 类别中选择 "Cascading Style Sheet"(级联样式表)。)
  2. 在 "CSS File Name"(CSS 文件名)文本字段中,键入 stylesheet。单击 "Finish"(完成)。

    此时会创建新文件,并在编辑器中打开。

  3. 在编辑器的 stylesheet.css 中键入以下规则。您可以使用 IDE 的代码完成支持,方法是:在希望查看建议时按 Ctrl-空格组合键。
    body {
       font-family: Verdana, Arial, sans-serif;
       font-size: smaller;
       padding: 50px;
       color: #555;
       width: 650px;
    }
    
    h1 {
       letter-spacing: 6px;
       font-size: 1.6em;
       color: #be7429;
       font-weight: bold;
    }
    
    h2 {
       text-align: left;
       letter-spacing: 6px;
       font-size: 1.4em;
       color: #be7429;
       font-weight: normal;
       width: 450px;
    }
    
    table {
       width: 550px;
       padding: 10px;
       background-color: #c5e7e0;
    }
    
    td {
       padding: 10px;
    }
    
    a {
      color: #be7429;
      text-decoration: none;
    }
    
    a:hover {
      text-decoration: underline;
    }
    
    .popupBox {
      position: absolute;
      top: 170px;
      left: 140px;
    }
    
    .popupCell {
       background-color: #fffafa;
    }
    
    .popupCell:hover {
      background-color: #f5ebe9;
    }
    
    .popupItem {
      color: #333;
      text-decoration: none;
      font-size: 1.2em;
    }
  4. 选择 "Window"(窗口)> "Web" > "CSS Styles"(CSS 样式),打开 "CSS Styles"(CSS 样式)窗口。
    &quot;CSS Styles&quot;(CSS 样式)窗口显示 h1 规则属性

    使用 "CSS Styles"(CSS 样式)窗口可以快速查看属性和编辑样式规则。当在 "CSS Styles"(CSS 样式)窗口的上方窗格中选择规则时,您可以在下方窗格中查看该规则的属性。单击上方窗格工具栏中的 "Edit CSS Rules"(编辑 CSS 规则)图标 (&quot;New CSS Property&quot;(新建 CSS 属性)图标),可以为样式表添加 CSS 规则。通过编辑属性表单可以在下方窗格中修改规则,通过单击下方窗格工具栏中的 "Add Property"(添加属性)图标 (&quot;New CSS Property&quot;(新建 CSS 属性)图标) 可以添加属性。

  5. 切换到源代码编辑器中的索引页,并在 <head> 标记之间添加对样式表的引用。
    <link rel="stylesheet" type="text/css" href="stylesheet.css">
  6. 将样式表中定义的 popupBox 类添加到 complete-table 元素中(更改的内容以粗体显示)。
    <tr>
        <td id="auto-row" colspan="2">
            <table id="complete-table" class="popupBox" />
        </td>
    </tr>

    使用编辑器中的代码完成可以帮助您选择要应用于选择器的样式规则。

    编辑器中的 CSS 代码完成

    stylesheet.css 中所示,此规则确定 complete-table 元素的位置,以便该元素显示在略靠其父元素右侧的位置。

保存索引页时,应用程序会自动重新部署到服务器。如果仍在浏览器中打开该页,则可以重新加载该页,您可以看到此时该页根据 CSS 样式表中的规则呈现。


运行项目

当再次运行该应用程序时,它将使用刚才创建的样式表显示在浏览器中。每次您键入字符时,都会向服务器发送异步请求,并返回 AutoCompleteServlet 准备好的 XML 数据。随着您输入的字符增多,为了反映匹配项的新列表,作曲家名字的数量会越来越少。

使用 HTTP 服务器监视器

您可以使用 IDE 的 HTTP 服务器监视器来验证在客户端与服务器之间传递请求和响应时发生的 HTTP 通信。HTTP 服务器监视器显示以下信息:如客户端和服务器头、会话属性、cookie 详细信息以及请求参数。

必须先在您使用的服务器上启用 HTTP 监视器,然后才能开始使用它。

  1. 通过从主菜单中选择 "Tools"(工具)> "Servers"(服务器)打开 "Servers"(服务器)窗口。
  2. 在左窗格中,选择用于该项目的服务器。然后,在右窗格中,选择 "Enable HTTP Monitor"(启用 HTTP 监视器)选项。

    注:对于 GlassFish Server,此选项显示在 "Common"(通用)标签下。对于 Tomcat,则位于 "Connection"(连接)标签下。

  3. 单击 "Close"(关闭)。

如果服务器已经运行,则必须重新启动服务器,更改才能生效。您可以通过以下方式重新启动服务器:打开 "Services"(服务)窗口("Window"(窗口)> "Services"(服务)),然后在 "Servers"(服务器)节点下右键单击您的服务器,并选择 "Restart"(重新启动)。

现在,当再次运行应用程序时,HTTP 监视器在 IDE 下部区域中打开。您可以在左窗格中选择一条记录,然后在主窗口中单击标签来查看与每个请求相关的信息。

HTTP 服务器监视器显示在 IDE 中

当用户在自动完成字段中输入字符时,您可以验证作为异步请求的结果从服务器发送的 XML 数据。

  1. 在 HTTP 监视器左侧的树视图中,右键单击一个请求记录并选择 "Replay"(重新显示)。

随即在您的浏览器中生成响应。在本例中,由于响应中包含 XML 数据,因此浏览器将在其本机 XML 查看器中显示数据。

HTTP 服务器监视器显示在 IDE 中

小结

以下内容对 Ajax 简介进行了小结。希望现在您已经明白了 Ajax 只是在后台通过 HTTP 交换信息,并基于结果动态地更新该页面。

您可能注意到,构建的应用程序存在许多缺点,例如,从自动完成框中选择作曲家姓名时无任何响应!欢迎您下载解决方案项目,以了解如何使用 JSP 技术实现这一点。此外,您可能希望研究如何通过服务器端验证来阻止用户请求数据存储中不存在的姓名。通过 Java EE 和 Java Web 学习资源中的其他教程,您可以进一步学习这些技巧和技术。


另请参见

有关 netbeans.org 中的 Ajax 和 Java 技术的更多信息,请参见以下资源:

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo