Struts2的官方文檔看似琳瑯滿目,但實(shí)際上并不完備,許多細(xì)節(jié)問(wèn)題并沒(méi)有深入涉及,部分內(nèi)容甚至還有錯(cuò)誤(可參見(jiàn):http://m.tkk7.com/usherlight/archive/2008/12/30/249143.html)。這次主要是補(bǔ)充一下,使用了ZeroConfig插件情況下,如何返回Stream類型的配置。
在ZeroConfig+CodeBehind結(jié)合使用的情況下,Struts.xml中基本上已經(jīng)空無(wú)一物了。配置基本上使用Annotation在Java的類中注釋完成。
@Parent("default")
@Result(name="rawFile", value="inputStream", type=StreamResult.class, params={"contentType", "text/html", "inputName", "inputStream", "", "", "bufferSize", "1024"})
public class FileDownloadAction {
public String execute() throws Exception {
inputStream = new FileInputStream("c:\\temp\\file.txt");
}
private InputStream inputStream;
//... getter and setter
}
這里最重要的是Result里面params的寫法,params后面的大括號(hào)中,奇數(shù)個(gè)字符串是key值,偶數(shù)個(gè)字符串是value值。
其中,inputName的值-inputStream要和Class中的InputStream的屬性名一致,而Result中的value的值也要與之一致。
<s:url id="testUrlId" namespace="/subscriber" action="customField" method="delete">struts2的文檔一直被大家所指責(zé),看來(lái)這種指責(zé)不無(wú)道理啊
<s:param name="customFieldDefinition.id" value="${id}"/>
</s:url>
經(jīng)過(guò)嘗試發(fā)現(xiàn)實(shí)際上應(yīng)該這樣寫: <s:param name="customFieldDefinition.id" value="id"/>
* 我首先嘗試使用JSF。
一開(kāi)始選擇JSF的原因主要是:
1、Stuts2自己提供了JSF的Plugin
2、JSF是Sun作為標(biāo)準(zhǔn)提出,而且已經(jīng)通過(guò)的。從Google的趨勢(shì)搜索上也可以看出,搜索JSF的人在增多。
3、JSF作為一種以組件為基礎(chǔ)的Web Framework有其獨(dú)到之處,其內(nèi)建的和其他許多開(kāi)源的組件使用起來(lái)相當(dāng)方便、強(qiáng)大。當(dāng)然,對(duì)于不同的應(yīng)用來(lái)說(shuō)也有不利之處(后面會(huì)提到),但是如果能夠堅(jiān)持長(zhǎng)期使用,逐漸積累組件庫(kù)的話,JSF是一個(gè)很好的選擇。
4、JSF的文檔(或者說(shuō)是書籍)還是比較多的。
經(jīng)過(guò)測(cè)試使用后,發(fā)現(xiàn)其性能與Struts2相比確實(shí)提升不少。但是后來(lái)遇到了一個(gè)問(wèn)題,所以最后還是放棄了JSF。這個(gè)問(wèn)題是關(guān)于JSF的DataTable的,JSF提供的DataTable其實(shí)使用起來(lái)很方便,可定制化程度也不錯(cuò),只是剛好缺少了我所希望的功能(也可能是我不知道如何實(shí)現(xiàn))。我的應(yīng)用中的DataTable是一個(gè)動(dòng)態(tài)的結(jié)果集,也就是說(shuō)輸出的列是不能預(yù)先確定的,而DataTable卻要求先聲明好所有的DataColumn,我不知道如何解決這個(gè)問(wèn)題。所以最后放棄了JSF。
* 我的第二個(gè)選擇是FreeMarker
選擇FreeMarker的原因是:
1、FreeMarker是Struts2缺少的模板引擎,Struts2的標(biāo)簽大部分是使用FreeMarker的,使用FreeMarker的話,連Plugin都省去了。
2、FreeMarker相對(duì)比較輕量級(jí)、因?yàn)樗旧碇皇且粋€(gè)模板引擎,與JSF這樣一個(gè)大而全的WebFramework相對(duì),輕巧多了。
3、FreeMarker的學(xué)習(xí)起來(lái)非常容易,只要把他網(wǎng)站上的Document過(guò)一遍,基本上就OK了。
4、FreeMarker雖然體積小,功能還是相當(dāng)強(qiáng)的,I18N,Converter之類的東西基本都全了,至少我所需要的功能全有。
5、FreeMarker相當(dāng)靈活,他不象JSF把底層的東西封裝了以后,暴露出一些屬性可以設(shè)置,如果你需要的屬性不能設(shè)置,你就沒(méi)有辦法了。在FreeMarker你直接操作最底層的東西,擁有很大的靈活性。當(dāng)然,犧牲了一些方便性,比如,要用FreeMarker生成一個(gè)下拉框,就需要較多的工作量了。
測(cè)試之后,使用FreeMarker的性能很不錯(cuò),在大數(shù)據(jù)量操作的情況下,至少一個(gè)數(shù)量級(jí)的性能提升。
主要原因是freeMarker的值直接從action中取得的,所以避開(kāi)了ognl的stack value.
* 我的最終結(jié)論,如果要在Struts2中,展示或者操作大量數(shù)據(jù),強(qiáng)烈推薦使用FreeMarker。
<package name="demo" extends="struts-default">
<action name="menu" method="execute" class="demo.TreeMenuAction">
<result>/WEB-INF/menu.jsp</result>
</action>
</package>
</struts>
二、tree.jsp
上面的JavaScript基本上是從dhtmlx的例子中修改而來(lái),理解起來(lái)并不復(fù)雜,只有
String.prototype._dhx_trim = function(){
return this.replace(/ /g," ").replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g,"");
}
這一段代碼含義不明。
三、Action
四、menu.jsp
<%@ page contentType="text/xml;charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<
s:property value="menuXmlString" escape="false"/>4. 使用FreeMarker的最新版本2.3.13,因?yàn)樵诎姹?.3.11中,F(xiàn)reeMarker針對(duì)性能進(jìn)行了改進(jìn),以下是FreeMarker2.3.11的release notes:
2.3.11
Date of release: 2007-12-04
This release contains several performance and usability improvements.
5. ognl2.7
所稱ognl2.7相對(duì)于2.6在性能上有了“顯著”的提升,于是下載了2.7以及2.7所需要的javassist-3.8.0.GA.jar
其實(shí),經(jīng)過(guò)上面2個(gè)步驟,我并沒(méi)有發(fā)現(xiàn)應(yīng)用的性能有顯著的改善,可能我的頁(yè)面中從ValueStack中的存取操作并不是特別多,也不是特別的復(fù)雜,所以,Ognl對(duì)我的影響并不明顯。
6. 最后使用了JProfiler對(duì)Tomcat進(jìn)行了監(jiān)控,最后發(fā)現(xiàn)問(wèn)題在自定義模板上,我將頁(yè)面的自定義模板全部刪除,果然頁(yè)面的響應(yīng)速度有了較大的提升。
在利用網(wǎng)頁(yè)展示查詢結(jié)果,經(jīng)常會(huì)遇到要求導(dǎo)出成Excel的需求。采用這種方法可以定制輸出的格式和內(nèi)容(還不支持合并單元格和公式),生成真正的Excel格式(不是csv)的Excel。
一、struts.xml
<?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>
<constant name="struts.i18n.encoding" value="UTF-8"/>
<package name="demo" extends="struts-default">
<action name="excel" method="execute" class="demo.ExcelAction">
<result name="excel" type="stream">
<param name="contentType">application/vnd.ms-excel</param> <!-- 注意這里的ContentType -->
<param name="inputName">excelStream</param> <!-- 這里需要和Action里的變量名一致 -->
<param name="contentDisposition">filename="standard.xls"</param>
<param name="bufferSize">1024</param>
</result>
</action>
</package>
</struts>
二、Struts2的 Action
package demo;
public class ExcelAction {
private InputStream excelStream; // 需要生成getter和setter
public String execute() throws Exception {
StringBuffer excelBuf = new StringBuffer();
excelBuf.append("BookName").append("\t").append("Year").append("\t").append("author").append("\n");
excelBuf.append("Thinking in Java").append("\t").append("2001").append("\t").append("Eckel").append("\n");
excelBuf.append("Spring in action").append("\t").append("2005").append("\t").append("Rod").append("\n");
String excelString = excelBuf.toString();
logger.debug("result excel String: " + excelString);
excelStream = new ByteArrayInputStream(excelString.getBytes(), 0, excelString.length());
return "excel";
}
// getter and setter
...
}
三、Jsp頁(yè)面
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<s:head />
</head>
<body>
<s:form action="" method="post">
<s:submit key="button.submit"/>
</s:form>
</body>
</html>
這時(shí)候,可以定義一個(gè)名為:sessionTimeout的global results
<result
name="sessionTimeout">/WEB-INF/pages/session_timeout.jsp</result>這樣,所有Action的session timeout都會(huì)被定向到指定的頁(yè)面
這是一個(gè)POJO,getter和setting省略了。
action中可以這樣使用:
查閱了struts2的文檔,發(fā)現(xiàn)radio標(biāo)簽有一個(gè)value屬性是用于對(duì)radio的進(jìn)行預(yù)選的: http://struts.apache.org/2.x/docs/radio.html
value: Preset the value of input element.
于是,進(jìn)行了試驗(yàn),<s:radio name="sex" list="#{'male','female'}" label="%{getText('app.label.sex')}" value="male" />
結(jié)果失敗了。male的值并沒(méi)有被選中,經(jīng)過(guò)反復(fù)研究,終于得到了正確的結(jié)果:
<s:radio name="sex" list="#{'male','female'}" label="%{getText('app.label.sex')}" value="'male'" />
看到其中的區(qū)別了嗎,就是多了兩個(gè)單引號(hào)。
我認(rèn)為這是因?yàn)関alue屬性的特性引起的。如果male沒(méi)有加引號(hào),那么struts2會(huì)去值的堆棧中尋找變量名為male的值,結(jié)果找不到。
加上單引號(hào)后,struts2(應(yīng)該是ognl)把'male'認(rèn)為是一個(gè)簡(jiǎn)單的字符串。
這樣,radio就能夠正確地匹配到值,使指定的值默認(rèn)被選中