`
sunxboy
  • 浏览: 2869327 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

DWR、Java 和 Dojo 工具箱集成 Java 和 JavaScript

阅读更多

2008 年 8 月 29 日

您能很快地说出多少 Java™ Web 开发框架、库和工具箱?没错,数量太多,以至于很难弄清楚它们各自的功能以及哪个功能可以真正帮助您解决问题。但是,如果您从事的是 Ajax 开发,那么您必须要知道这个库:Direct Web Remoting (DWR)。它利用 Java 语言和 Java Web 技术大大地简化了 Ajax 开发,并为如何无缝地将 Ajax 集成到 Java Web 应用程序设立了标准。实际上,DWR 加入了 Dojo Foundation,后者集合了许多流行的开源 Ajax 技术。在本文中,了解使用 DWR 轻松开发 Ajax。

本文是包含 3 个部分的系列文章的第 3 部分,也是最后一个部分,这个系列文章介绍了创建 Ajax 支持的应用程序所需的流行 JavaScript 库。在 第 1 部分 ,您了解了如何使用 Prototype 库构建一个用来管理歌曲的 Web 应用程序。第 2 部分 讨论了如何使用 Scriptaculous 库构建一个用来管理照片的 Web 应用程序。本文侧重于向您展示如何利用 DWR 简化 Ajax 开发。

Ajax 资源中心

请访问 Ajax 资源中心 ,这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。

本 文使用 DWR 2.0 版本。由于使用了泛型和注释,所以示例代码要求 Java 5+。示例应用程序结合使用了 MySQL 5.12 和 Tomcat 6.0.14。当然,您应该能够轻松切换到其他实现。应用程序还使用了 Java Persistence API (JPA) 和 OpenJPA 1.0,分别用于数据访问和应用程序的 JPA 实现。同样,您也应该能够切换到其他 JPA 实现(比如 Hibernate、Kodo 等)。本文使用了 Firefox 的 Firebug 插件,因为该插件是一个很棒的 Ajax 调试工具。在 参考资料 部分可以获得这些工具的相关链接。

Direct Web Remoting (DWR) 简介

Ajax 应用程序看上去有些神秘,幸好开发应用程序的过程还比较直观。对于每个 Ajax 交互,必须在服务器上创建一个端点(从从事 Web 服务的朋友那里借用的术语),并且还要创建客户端代码来调用该端点。此外,还必须创建用于序列化客户机和服务器之间的数据流的所有代码。这些服务器端点可 以是泛型服务,甚至可以是 REST 式的端点。但是,它们的创建通常特定于客户机的需要。有时应该避免紧密耦合,但有时又应该采用。对于后一种情况,DWR 是一种一揽子的解决方案。它允许您将服务器端代码作为 Ajax 端点公开,而所有模板都是自动生成的。现在,让我们通过一个特定的示例来看看 DWR 是如何工作的。

 




回页首


示例应用程序:Ajax 留言板

这里使用的示例应用程序是一个简单的留言板。数据模型已经大大简化,以便您能把精力放在借助 DWR 的 Ajax 交互。我们先来研究一下应用程序的后端,看看 DWR 如何能层叠于其上以启用应用程序的 Ajax 功能。

 




回页首


设置后端

先为留言板创建一个数据库表。清单 1 给出了创建此表所需的 SQL 脚本。


清单 1. 留言表的 SQL 脚本

				
CREATE TABLE 'messages' (
  'id' int(11) NOT NULL auto_increment,
  'title' varchar(40) NOT NULL,
  'body' text,
  'created_at' timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  'author' varchar(40) NOT NULL default 'anonymous',
  PRIMARY KEY  ('id')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 

这里需要特别注意的一点是:需要为留言板上的每个消息提供标 题、主体和作者。SQL 脚本会完成除此之外的其他工作。所显示的脚本是针对 MySQL 的,但稍微调整便可用于其他 RDBMS 的 SQL。对于数据访问,我使用的是 JPA。此表对应的 Java 类如清单 2 所示。


清单 2. 留言 Java 类

				
package org.developerworks.msgb;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Column;

import org.directwebremoting.annotations.DataTransferObject;

@DataTransferObject
@Entity
@Table(schema="msgb", name = "messages")
public class Message {
     @Id
     @GeneratedValue(strategy=IDENTITY)
     private int id;
     
     @Column(name="title")
     private String title;
     
     @Column(name="author")
     private String author;
     
     @Column(name="body")
     private String body;
     
     @Column(name="created_at")
     private Date createdAt;

     public int getId() {
          return id;
     }

     public void setId(int id) {
          this.id = id;
     }

     public String getTitle() {
          return title;
     }

     public void setTitle(String title) {
          this.title = title;
     }

     public String getAuthor() {
          return author;
     }

     public void setAuthor(String author) {
          this.author = author;
     }

     public String getBody() {
          return body;
     }

     public void setBody(String body) {
          this.body = body;
     }

     public Date getCreatedAt() {
          return createdAt;
     }

     public void setCreatedAt(Date createdAt) {
          this.createdAt = createdAt;
     }
}

 

这段代码的大部分都是样本文件代码:几个字段和一些 getter 和 setter 方法。而且,大多数注释都只是一些标准的 JPA 注释,它可将 Java 字段影射到数据库列。需要注意其中的一个异常注释,即 @DataTransferObject 。它是一个 DWR 注释,告知 DWR 此类可以自动整理,并作为 Ajax 响应的一部分发送。DWR 包括几个整理 Java 类型的转换器。也可以随意指定 DWR 包括/不包括哪些字段,这同样可以通过使用注释实现。设置好数据后,就可以为应用程序创建服务了。

 




回页首


创建远程服务

示例应用程序主要完成两个简单的目标:一是列出张贴到留言板上的所有消息,二是允许用户向留言板张贴消息。参见清单 3,它给出了此应用程序的服务代码。


清单 3. MessageService

				
package org.developerworks.msgb;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;

@RemoteProxy
public class MessageService {
     private EntityManagerFactory factory;
     private EntityManager em;
     
     public MessageService(){
          factory = Persistence.createEntityManagerFactory("msgb");
          em = factory.createEntityManager();
     }
     
     @RemoteMethod
     @SuppressWarnings("unchecked") // thanks type erasure
     public List<Message> getMessages(){
          List<Message> messages = em.createQuery("select m from 
		                                  Message m").getResultList();
          return messages;
     }
     
     @RemoteMethod
     public void saveMessage(Message msg){
          em.getTransaction().begin();
          em.persist(msg);
          em.getTransaction().commit();
     }
}

 

这里所示的代码是一些用来查询和创建消息的标准 JPA 代码。同样,这里的注释代码也很有趣。这个类被注释为 RemoteProxy 。这就告诉 DWR 远程客户机可以调用该类的方法。当然,这些远程调用的机制是借助 DWR 的 Ajax。通过 RemoteMethod 注释显式地将该类的每个方法公开给远程客户机。如果不想公开某些方法,省略相应的注释即可。至此我们所写的所有代码都是一些后端代码和注释。要在后端设置 DWR,还需要做另一件事情。

 




回页首


DWR servlet

DWR 使用 Java servlet 接受和响应 Ajax 请求。这个 servlet 包括在 DWR 库内,因此只需配置 Web 应用程序,以便在应用程序的 web.xml 文件内启用 servlet。清单 4 给出了示例应用程序的 web.xml。


清单 4. 留言板 web.xml

				
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     id="msgb" version="2.5">
     <display-name>MessageBoard</display-name>
     <welcome-file-list>
          <welcome-file>index.html</welcome-file>
     </welcome-file-list>
     <servlet>
          <display-name>DWR Servlet</display-name>
          <servlet-name>dwr-invoker</servlet-name>
          <servlet-class>
               org.directwebremoting.servlet.DwrServlet
          </servlet-class>
          <init-param>
               <param-name>debug</param-name>
               <param-value>true</param-value>
          </init-param>
          <init-param>
               <param-name>classes</param-name>
               <param-value>org.developerworks.msgb.MessageService, 
			   org.developerworks.msgb.Message</param-value>
          </init-param>
     </servlet>
     <servlet-mapping>
          <servlet-name>dwr-invoker</servlet-name>
          <url-pattern>/dwr/*</url-pattern>
     </servlet-mapping>
</web-app>

 

上述代码公开了 DWR servlet 并向其发送所有以 /dwr/ 开始的 URL。此外,请注意 init-paramsclasses 参数是一个以逗号分隔的列表,它由带有 DWR 注释的所有类组成。注意:如果不喜欢注释,也可以使用 XML 文件来获取相同的信息。这就是在后端需要做的所有工作,现在可以开始在应用程序的前端使用 DWR 了。

 




回页首


构建前端

DWR 使在应用程序的后端启用 Ajax 变得十分容易。需要做的仅是添加一些声明和激活一个 DWR servlet。但前端的情况又如何呢?可以使用大多数 JavaScript 工具箱的某些库。通常,要在 Web 页面内引用这些库,然后阅读有关文档了解如何使用它们。不过这不适用于 DWR。

DWR 会自动生成 JavaScript。所幸的是,借助 DWR 能够轻松了解如何使用该 JavaScript。回顾一下清单 4 中的 web.xml 文件。注意到 init-param 调用的调试了吗?这一设置允许自检 DWR 创建的动态 DWR JavaScript。简单部署 Web 应用程序后转到以下的 URL: http://<root>/<web_app_name>/dwr,如图 1 所示。


图 1. DWR 调试屏幕
DWR 调试屏幕

该图显示出了用 @RemoteProxy 声明标注的所有 Java 类。单击任何一个类,就会显示相关细节,如图 2 所示。


图 2. MessageService JavaScript 信息
MessageService JavaScript 信息

这个界面展示了如何在 Web 页面上引用动态 JavaScript。这里有一个必须包含的核心库(engine.js),以及一个特定于此应用程序的纯动态库(MessageService.js)。这里的一个可选实用工具库提供了许多 helper 函数。

掌握了如何引用 DWR JavaScript 后,还需知道如何使用它。可以使用 MessageBoard 页面测试它,如图 3 所示。


图 3. 测试 DWR Ajax
测试 DWR Ajax

此图展示了 getMessages() 调用的 Ajax 请求的结果。所显示的数据是一个 JavaScript 对象(JSON)的数组。我们知道了 API 将要返回的内容,但如何调用它呢?我们看看测试页面的源代码,如清单 5 所示。


清单 5. MessageBoard 测试页面的源代码

				
<li>
  getMessages(  );
  <input class='ibutton' type='button' onclick='MessageService.getMessages(reply0);'
     value='Execute'  title='Calls MessageService.getMessages(). 
     View source for details.'/>
  <script type='text/javascript'>
    var reply0 = function(data)
    {
      if (data != null && typeof data == 'object') 
	            alert(dwr.util.toDescriptiveString(data, 2));
      else dwr.util.setValue('d0', dwr.util.toDescriptiveString(data, 1));
    }
  </script>
  <span id='d0' class='reply'></span>
</li>
<li>
  saveMessage(    <input class='itext' type='text' size='10' value='' 
    id='p10' title='Will be converted to: org.developerworks.msgb.Message'/>  );
        <input class='ibutton' type='button' onclick='MessageService.saveMessage
       (objectEval($("p10").value), reply1);' value='Execute'  title='Calls 
	    MessageService.saveMessage(). View source for details.'/>
  <script type='text/javascript'>
    var reply1 = function(data)
    {
      if (data != null && typeof data == 'object') alert(dwr.util.
	                                                 toDescriptiveString(data, 2));
      else dwr.util.setValue('d1', dwr.util.toDescriptiveString(data, 1));
    }
  </script>

  <span id='d1' class='reply'></span>
</li>

 

因此,比如要调用 getMessages ,则可以使用 MessageService.getMessages(callbackFunction) 。测试该方法时,您将看到传递给 callbackFunction 的参数。现在可以构建 Web 页了,如清单 6 所示。


清单 6. MessageBoard Web 页面

				
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
 <html>
<head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>The developerWorks Message Board</title>
      <script type="text/javascript" src="/MessageBoard/dwr/interface/
                                      MessageService.js"></script>
      <script type="text/javascript" src="/MessageBoard/dwr/
                                      engine.js"></script>
      <script type="text/javascript" src="/MessageBoard/dwr/
                                      util.js"></script>
      <script type="text/javascript">
           var columns = ["title", "author", "body", "createdAt"];
           function loadData(){
                MessageService.getMessages(addRows);
         }
           // messages should be an array of Message hashes
           function addRows(messages){
                var cellfuncs = [];
                for each (var prop in columns){
                     cellfuncs.push(createFunction(prop));
                }
                dwr.util.addRows("messageTableBody", messages, cellfuncs);     
           }
           function createFunction(prop){
                return function(msg) { return msg[prop]; };
           }
           function sendMsg(){
                var msg = {};
                msg.title = dwr.util.getValue("title");
                msg.author = dwr.util.getValue("author");
                msg.body = dwr.util.getValue("body");
                MessageService.saveMessage(msg);
                msg.createdAt = Date();
                var messages = [msg];
                addRows(messages);
                clearForm();
           }
           function clearForm(){
                dwr.util.setValue("title","");
                dwr.util.setValue("author","");
                dwr.util.setValue("body","");
           }
      </script>     
      <style type="text/css">
           label {
               float: left;
               text-align: right;
               margin-right: 15px;
               width: 100px;
          }
          #messageTableHead {
               font-weight: 900;
               color:navy;
          }
          body {
               color:MidnightBlue;
               background-color:PaleTurquoise;
               margin:20px;
               padding:0px;
               font:11px verdana, arial, helvetica, sans-serif;
          }
          .pageTitle {
               margin:0px 0px 15px 0px;
               padding:0px;
               font-size:28px;
               font-weight:900;
               color:#aaa;
          }
          #formDiv{
               padding-top:12px;
               color:Indigo;
          }
      </style>
</head>
<body onLoad="loadData()">
     <div class="pageTitle">The developerWorks Message Board</div>
     <div class="pageContent">
          <table id="messageTable" border="1" cellpadding="4">
               <thead id="messageTableHead">
                    <tr>
                         <td>Title</td>
                         <td>Author</td>
                         <td>Message</td>
                         <td>Posted</td>
                    </tr>
               </thead>
               <tbody id="messageTableBody">
               </tbody>
          </table>
     </div>
     <div id="formDiv">
          <form id="messageForm">
               <label>Message Title:</label><input type="text" 
	                                name="title" id="title"/><br/>
               <label>Your Name:</label><input type="text"
	                              name="author" id="author"/><br/>
               <textarea name="body" cols="80" rows="15"
	                              id="body"></textarea><br/>
               <input type="button" id="msgBtn" value="Add Message"
	                              onClick="sendMsg()"/>
          </form>
     </div>
</body>
</html>

 

我们分析一下这段代码。首先,它是一个简单的 HTML 文件。它不是 JSP 或 JSF 页面,而是具有一些 JavaScript 和 CSS 的静态 HTML。加载这个页面时,调用 loadData() 函数。该函数使用刚刚介绍过的 MessageService.getMessages() 调用。它传入 addRows 函数,以便处理 DWR 发出的 Ajax 请求的响应。

addRows() 函数接受服务器返回的数据并使用 DWR 提供的一种实用工具函数。该函数是 dwr.util.addRows ,它接受 HTML 表、表头、表脚或表体的 ID,并向它们追加行。它使用了一个函数(此段代码中的 cellfuncs )数组,其中的每个函数都用来压缩或转换数据,以便将数据放入表的单元格内。因此,此表的第 (i,j) 个元素内的数据将会是 cellfuncs[j](messages[i]) 。这是一种将数据影射到表的简单方法。在浏览器内显示此页面,效果类似图 4。


图 4. MessageBoard 页面
MessageBoard 页面

该页使用支持 DWR 的 Ajax 异步加载数据。因此能够显示消息,但如何保存新消息呢?这可通过页面底部的表单来实现。单击 Add Message 时,使用 DWR 的实用方法 dwr.util.getValue 从表单获得数据。此实用工具函数适用于任何 HTML 元素,包括 div 、文本输入、复选框和选择列表。获得数据后,将其放入 JavaScript 对象并调用 MessageService.saveMessage() 。在本例中,由于响应为空,所以没有指定处理函数,而是重用了前面的 addRows 函数,用来向表追加新行。这使 UI 的响应性变得很好。

填写表单并单击 Add Message ,可以测试该页。使用类似 Firebug 的工具可以查看 HTTP 流量。清单 7 给出了 saveMessage 调用发送的数据。


清单 7. saveMessage 调用

				
callCount=1
page=/MessageBoard/
httpSessionId=
scriptSessionId=B88B0681A9BB674C14786B7DCA3EA6E3153
c0-scriptName=MessageService
c0-methodName=saveMessage
c0-id=0
c0-e1=string:The%20One%20I%20Love
c0-e2=string:Michael
c0-e3=string:This%20goes%20out%20to%20the%20one%20I%20love.
                  %20This%20one%20goes%20out%20to%20the%20one
%20I%20left%20behind.
c0-param0=Object_Object:{title:reference:c0-e1, 
                  author:reference:c0-e2, body:reference:c0-e3}
batchId=1

 

图 7 展示了 DWR 发送的数据的格式。它不是那么重要 — 像 DWR 这样的框架的目的之一便是解决格式问题。但了解一下它的工作原理也是很有意思的。

 




回页首


结束语

很 多 Ajax 工具箱都能简化 Ajax 开发的不同部分。DWR 是一种面向 Java Web 应用程序的端对端工具箱,它简化了应用程序服务器端和客户端的创建。服务器端只需使用一些 Java 注释将服务转变成 Ajax 服务。而在客户端,则使用一个 API 影射服务器上的内容。您仅需要添加一个回调函数。没有比这更简单的了!





回页首


下载

描述 名字 大小 下载方法 第 3 部分示例代码
wa-aj-ajaxpro3.zip 1087KB HTTP

分享到:
评论

相关推荐

    使用DWR框架简化Ajax开发

    DWR现在已经在java平台的AJAX应用中使用比较广泛,下面将以前项目中用到的部分内容(测试部分)贴出来,以供参考

    dwr,dojo框架应用实例

    DWR 是一个开源JavaScript库,允许Web应用程序在客户端和服务器之间进行实时通信,即实现Ajax(异步JavaScript和XML)功能。它简化了JavaScript与Java后端服务的交互,让开发者能够调用服务器上的方法,就像它们是...

    dwr和java整合完整版例子

    Direct Web Remoting (DWR) 是一个开源的Java库,它允许JavaScript在浏览器和服务器之间进行实时通信,绕过通常的Ajax限制。这个"dwrc和java整合完整版例子"是一个专门为初学者设计的教程,旨在帮助他们理解和实现...

    DWR js框架 javascript框架 web与java交互 Direct Web Remoting Ajax开源框架

    Direct Web Remoting (DWR) 是一个开源的Java库,它允许Web应用程序在客户端的JavaScript和服务器端的Java之间进行直接的、异步的通信,实现了Web应用中的Ajax功能。DWR通过自动化处理JavaScript和Java之间的类型...

    springboot整合dwr实现js调用java方法

    通过DWR,开发者可以在不涉及繁琐的AJAX请求和响应处理的情况下,直接在JavaScript中调用Java对象的方法,使得前端和后端的通信变得更为便捷。 **整合SpringBoot与DWR** 1. **添加依赖**:在SpringBoot项目的`pom....

    DWR:java ajax application

    DWR (Direct Web Remoting) 是一种基于Ajax的Java框架,它简化了JavaScript与Java之间的通信过程,使得开发者能够更加高效地构建富客户端应用程序。 #### 二、DWR简介 DWR由Joe Walker创建,首次发布于2003年,是...

    dwr实例,JavaScript调用java方法的小例子

    Direct Web Remoting (DWR) 是一种开源的Java框架,它允许Web应用程序在客户端JavaScript和服务器端Java之间进行异步通信。DWR简化了AJAX(Asynchronous JavaScript and XML)应用的开发,使得开发者可以方便地在...

    dwr测试,java调用js

    Direct Web Remoting (DWR) 是一个开源的Java库,它允许Web应用程序在客户端JavaScript和服务器端Java之间进行异步的、动态的交互。DWR的主要目标是简化AJAX(Asynchronous JavaScript and XML)开发,使得开发者...

    dwr和ssh的集成源码

    **DWR(Direct Web Remoting)** 是一种Java库,允许JavaScript和Java之间进行安全、动态的远程方法调用。它使得Web应用可以实现实时的双向通信,让客户端和服务器端能够实时交换数据,极大地增强了用户体验。 **...

    dwr java推送例子 免积分

    Direct Web Remoting (DWR) 是一个开源的Java库,它允许JavaScript在浏览器和服务器之间进行交互,从而实现Ajax功能。这个"免积分"的例子可能是为了帮助开发者理解如何在DWR框架下实现实时的数据推送,而无需通过...

    java 采用dwr框架构实现ajax

    使用DWR,开发者可以声明式地定义哪些Java类和方法可供JavaScript调用,DWR会自动生成必要的JavaScript接口。这使得在前端使用Ajax变得非常方便。 **四、DWR的使用步骤** 1. **配置DWR**: 在Web应用的`web.xml`中...

    ajax 框架 dwr java

    - **Ajax**:异步JavaScript和XML,通过局部刷新提高用户体验,DWR利用Ajax技术实现了页面无刷新交互。 - **Java API**:DWR提供了丰富的Java API,方便开发者在服务器端进行配置和处理。 - **JavaScript API**:在...

    java dwr 使用例子

    Java DWR(Direct Web Remoting)是一个开源框架,它允许JavaScript和Java在Web应用程序中进行交互,实现异步通信,即Ajax(Asynchronous JavaScript and XML)功能。DWR简化了客户端与服务器端之间的数据交换,使得...

    java_ajax框架dwr

    2. **Java远程调用(Remote Method Invocation, RMI)**:DWR模仿RMI的概念,实现了浏览器中的JavaScript和服务器上的Java方法之间的直接调用。 3. **映射机制**:DWR通过配置文件或注解将Java类和方法映射到...

    JavaScript:DWR的用法实例

    JavaScript Direct Web Remoting (DWR) 是一个开源的JavaScript库,它允许Web应用程序在客户端的JavaScript和服务器端的Java之间进行安全、简单的异步通信。这个框架极大地简化了AJAX(Asynchronous JavaScript and ...

    java dwr 框架源码

    `Configurator`配置DWR的行为,例如设置安全策略和映射Java类到JavaScript;`Hessian`是一种轻量级的远程调用协议,用于在HTTP上高效传输Java对象。 2. **DWR的工作原理**:DWR通过动态生成JavaScript库,使得...

    DWR技术文档、快速使用DWR、java技术

    DWR作为Java世界中的一个强大工具,极大地简化了AJAX开发,使得JavaScript能够轻松地与服务器进行通信。通过理解其核心功能、配置步骤以及使用流程,开发者可以快速地在项目中引入DWR,构建高效、动态的Web应用。

    DWR示例与spring集成

    **DWR(Direct Web Remoting)**是一种Java技术,它允许Web应用程序在浏览器和服务器之间进行实时通信,类似于Ajax(Asynchronous JavaScript and XML),但提供了更简单和直接的方法。DWR使得JavaScript可以直接...

    springMVC集成dwr

    DWR (Direct Web Remoting) 是一个开源的 JavaScript 和 Java 之间的远程调用框架,它允许在浏览器端直接调用服务器端的 Java 方法,实现了类似 AJAX 的功能,但更易于使用和调试。 **SpringMVC 集成 DWR 的关键...

Global site tag (gtag.js) - Google Analytics