Struts 2与AJAX
在当今——Web 2.0概念铺天盖地的Internet环境下,简易的AJAX集成对于一个成功的WEB框架来说是不可或缺的。因此,Struts 2其中的一个重要的功能(Feature)就是“First-class AJAX support - Add interactivity and flexibility with AJAX tags that look and feel just like standard Struts tags(大意:一流的AJAX支持——通过AJAX标志增加互动性和灵活性,而且使用这些AJAX标志与普通的Struts标志同样简单)”。
实现原理
基于不重新发明轮子的原则,Struts 2并没有开发新的AJAX框架,而是使用时下Java EE平台中比较流行的AJAX框架——Dojo和DWR。
最近在Musachy Barroso等同志的无私奉献下,开发了Struts 2的JSON插件(Plugin),极大地方便了我们输出JSON结果(Result)。
JSON插件(Plugin)
在Struts 2的showcase中的AJAX部分,JSON的结果输出是通过Freemaker模板实现。这种方法在简易性和灵活性上都比不上JSON插件,所以JSON插件值得向大家五星推荐。
下面让我们看一个JSON插件的例子。
首先到以下网址http://code.google.com/p/jsonplugin/downloads/list下载JSON插件的JAR包,并将其加入你的WebContent\WEB-INF\lib下。
接下是本例子的Action代码:
package tutorial;import java.util.ArrayList;import java.util.List;import com.googlecode.jsonplugin.annotations.JSON;import com.opensymphony.xwork2.ActionSupport;public class JsonPluginAction extends ActionSupport { private static final long serialVersionUID = -6784977600668791997L; private int bookId; private String title; private double price; private List<String> comments; private transient String secret1; private String secret2; @JSON(name="ISBN") public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public List<String> getComments() { return comments; } public void setComments(List<String> comments) { this.comments = comments; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @Override public String execute() { bookId = 15645912; title = "Max On Java"; price = 0.9999d; comments = new ArrayList<String>(3); comments.add("It's no bad!"); comments.add("WOW!"); comments.add("No comment!"); secret1 = "You can't see me!"; secret2 = "I am invisible!"; return SUCCESS; }}
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"><struts> <package name="Struts2_AJAX_DEMO" extends="json-default"> <action name="JsonPlugin" /> </action> </package></struts>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" ><head> <title>JSON Plugin</title> <script type="text/javascript"> var bXmlHttpSupport = (typeof XMLHttpRequest != "undefined" || window.ActiveXObject); if (typeof XMLHttpRequest == "undefined" && window.ActiveXObject) { function XMLHttpRequest() { var arrSignatures = ["MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; for (var i=0; i < arrSignatures.length; i++) { try { var oRequest = new ActiveXObject(arrSignatures[i]); return oRequest; } catch (oError) { /*ignore*/ } } throw new Error("MSXML is not installed on your system."); } } function retrieveBook() { if(bXmlHttpSupport) { var sUrl = 'JsonPlugin.action'; var oRequest = new XMLHttpRequest(); oRequest.onreadystatechange = function() { if(oRequest.readyState == 4) { var oBook = eval('(' + oRequest.responseText + ')'); var bookHolder = document.getElementById('bookHolder'); var sBook = '<p><b>ISBN: </b>' + oBook.ISBN + '</p>'; sBook += ('<p><b>Title: </b>' + oBook.title + '</p>'); sBook += ('<p><b>Price: </b>$' + oBook.price + '</p>'); sBook += ('<b><i>Comments: </i></b><hr/>'); for(i = 0; i < oBook.comments.length; i++) { sBook += ('<p><b>#' + (i + 1) + ' </b>' + oBook.comments[i] + '</p>'); } bookHolder.innerHTML = sBook; } }; oRequest.open('POST', sUrl); oRequest.send(null); } } </script></head><body> <input type="button" value="Retrieve Book" onclick="retrieveBook()" /> <div id="bookHolder"></div></body></html>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags"%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JSON Plugin</title> <s:head theme="ajax" /> <script type="text/javascript"> dojo.addOnLoad(function() { dojo.event.topic.subscribe('retrieveBook', this, function(data, type, e){ if(type == 'load') { showBook(data); } else if(type == 'error') { alert('Can not retrieve the book'); } }); }); function showBook(strBook) { var oBook = eval('(' + strBook + ')'); var bookHolder = document.getElementById('bookHolder'); var sBook = '<p><b>ISBN: </b>' + oBook.ISBN + '</p>'; sBook += ('<p><b>Title: </b>' + oBook.title + '</p>'); sBook += ('<p><b>Price: </b>$' + oBook.price + '</p>'); sBook += ('<b><i>Comments: </i></b><hr/>'); for(i = 0; i < oBook.comments.length; i++) { sBook += ('<p><b>#' + (i + 1) + ' </b>' + oBook.comments[i] + '</p>'); } bookHolder.innerHTML = sBook; } </script> </head> <body> <s:url id="bookUrl" value="/JsonPlugin.action" /> <s:submit href="%{bookUrl}" theme="ajax" indicator="indicator" value="Retrieve Book" align="left" notifyTopics="retrieveBook" /> <s:a theme="ajax" href="%{bookUrl}" indicator="indicator" notifyTopics="retrieveBook">Retrieve Book</s:a> <img id="indicator" src="${pageContext.request.contextPath}/images/indicator.gif" alt="Struts 二与AJAX" style="display:none" /> <div id="bookHolder"></div> </body></html>
package tutorial;import java.util.ArrayList;import java.util.List;public final class Datas { public static final List<String> NAMES; static { NAMES = new ArrayList<String>(); NAMES.add("Alabama"); NAMES.add("Alaska"); NAMES.add("American Samoa"); NAMES.add("Arizona"); NAMES.add("Arkansas"); NAMES.add("Armed Forces Europe"); NAMES.add("Armed Forces Pacific"); NAMES.add("Armed Forces the Americas"); NAMES.add("California"); NAMES.add("Colorado"); NAMES.add("Connecticut"); NAMES.add("Delaware"); NAMES.add("District of Columbia"); NAMES.add("Federated States of Micronesia"); NAMES.add("Florida"); NAMES.add("Georgia"); NAMES.add("Guam"); NAMES.add("Hawaii"); NAMES.add("Idaho"); NAMES.add("Illinois"); NAMES.add("Indiana"); NAMES.add("Iowa"); NAMES.add("Kansas"); NAMES.add("Kentucky"); NAMES.add("Louisiana"); NAMES.add("Maine"); NAMES.add("Marshall Islands"); NAMES.add("Maryland"); NAMES.add("Massachusetts"); NAMES.add("Michigan"); NAMES.add("Minnesota"); NAMES.add("Mississippi"); NAMES.add("Missouri"); NAMES.add("Montana"); NAMES.add("Nebraska"); NAMES.add("Nevada"); NAMES.add("New Hampshire"); NAMES.add("New Jersey"); NAMES.add("New Mexico"); NAMES.add("New York"); NAMES.add("North Carolina"); NAMES.add("North Dakota"); NAMES.add("Northern Mariana Islands"); NAMES.add("Ohio"); NAMES.add("Oklahoma"); NAMES.add("Oregon"); NAMES.add("Pennsylvania"); NAMES.add("Puerto Rico"); NAMES.add("Rhode Island"); NAMES.add("South Carolina"); NAMES.add("South Dakota"); NAMES.add("Tennessee"); NAMES.add("Texas"); NAMES.add("Utah"); NAMES.add("Vermont"); NAMES.add("Virgin Islands, U.S."); NAMES.add("Virginia"); NAMES.add("Washington"); NAMES.add("West Virginia"); NAMES.add("Wisconsin"); NAMES.add("Wyoming"); }}
package tutorial;import java.util.ArrayList;import java.util.List;import com.opensymphony.xwork2.ActionSupport;public class AutocompleterAction extends ActionSupport { private static final long serialVersionUID = -8201401726773589361L; private List<String[]> names; private String start; public void setStart(String start) { this.start = start; } public List<String[]> getNames() { return names; } @Override public String execute() { names = new ArrayList<String[]>(); if(start == null || "".equals(start.trim())) { start = "a"; } for(String s : Datas.NAMES) { if(s.toLowerCase().startsWith(start.toLowerCase())) { names.add(new String[]{ s, s }); } } return SUCCESS; }}
<action name="Autocompleter" name="code"><%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags"%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Struts 2 AJAX - Autocompleter</title> <s:head theme="ajax" /> </head> <body> <h2> Autocompleter </h2> <s:form action="autocompleterForm"> <s:textfield label="abc" name="abc" /> <tr> <td name="user" list="@tutorial.Datas@NAMES" /> </td> </tr> <tr> <td value="/Autocompleter.action" /> <s:autocompleter theme="ajax" name="start" href="%{dataUrl}" loadOnTextChange="true" loadMinimumCount="1" indicator="indicator" autoComplete="false" showDownArrow="false" /> <img id="indicator" src="${pageContext.request.contextPath}/images/indicator.gif" alt="Struts 二与AJAX" style="display:none" /> </td> </tr> </s:form> </body></html>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags"%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Struts 2 AJAX - Tree</title> <s:head theme="ajax" debug="true" /> <script type="text/javascript"> function treeNodeSelected(arg) { alert(arg.source.title + ' selected'); } dojo.addOnLoad(function() { var s = dojo.widget.byId('parentId').selector; dojo.event.connect(s, 'select', 'treeNodeSelected'); }); </script> </head> <body> <h2> Tree </h2> <div style="float:left; margin-right: 50px;"> <s:tree label="parent" id="parentId" theme="ajax" templateCssPath="/struts/tree.css" showRootGrid="true" showGrid="true"> <s:treenode theme="ajax" label="child1" id="child1Id"> <s:treenode theme="ajax" label="grandchild1" id="grandchild1Id" /> <s:treenode theme="ajax" label="grandchild2" id="grandchild2Id" /> <s:treenode theme="ajax" label="grandchild3" id="grandchild3Id" /> </s:treenode> <s:treenode theme="ajax" label="child2" id="child2Id" /> <s:treenode theme="ajax" label="child3" id="child3Id" /> <s:treenode theme="ajax" label="child4" id="child4Id" /> <s:treenode theme="ajax" label="child5" id="child5Id"> <s:treenode theme="ajax" label="gChild1" id="gChild1Id" /> <s:treenode theme="ajax" label="gChild2" id="gChild2Id" /> </s:treenode> </s:tree> </div> </body></html>