學習struts2建bbs總結七:extends ActionSupport帶來的ClassNotFoundException異常
這個問題可說是這次學習遇到的最怪異的問題了,一直在網上詢問了一週多直到現在均未得到答案,大概和高手緣分不深。哈哈。
這時我感覺寫總結對於學習是很有幫助的,自從寫寫這些學習總結,回顧的時候,我竟慢慢的發現了很多原來未曾發現的問題,並且都漸漸解決了。例如之前的prototype模式,jquery,等等。趁著有時間,就及時的寫出來,一則和遇到類似問題的朋友共享,二則避免自己以後犯同樣錯誤。
事情是這樣的:我在編寫程式的過程中,一些action我是這樣寫的:如
public class PostAction implements Action......
也就是說,我實現的是action介面,直到bbs專案結束,那時剛把那個分頁的宕機問題解決,我測試了整個bbs執行,是可以正常執行的。只是我無意間看到一些說起baseAction的文章,我自己之前的總結也寫了,所以我新建了baseAction類,如下:
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.interceptor.ServletRequestAware; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; public class BaseAction extends ActionSupport implements ServletRequestAware,SessionAware { protected HttpServletRequest request; protected Map<String, Object> session; public void setServletRequest(HttpServletRequest request) { this.request = request; } public void setSession(Map<String, Object> session) { this.session = session; } }
這個類的目的就是生成session以及request,好處就是避免每個action甚至每個方法都要寫上這句
Map session=ActionContext.getContext().getSession();
所以,我就把程式中的action,統統修改為extends BaseAction,但自從修改了這句後,程式遇到postAction發帖操作就報異常,我改為extends ActionSupport也是同樣報錯,如下:
post.jsp主要程式碼:
<FORM name="postForm" onSubmit="return check()" action="post.action?page=1" method="POST"> <INPUT id="a2" type="hidden" name="boardName" value="${fn:substring(bName,0,20)}"/> <INPUT id="a3" type="hidden" name="boardId" value=<%= session.getAttribute("boardId")%>/> <DIV class="t"> <TABLE cellSpacing="0" cellPadding="0" align="center"> <TR> <TD class="h" colSpan="3"><B>發表帖子</B></TD> </TR> <TR class="tr3"> <TH width="20%"><B>標題</B></TH> <TH><INPUT class="input" style="PADDING-LEFT: 2px; FONT: 14px Tahoma" tabIndex="1" size="60" name="title"></TH> </TR> <TR class="tr3"> <TH vAlign=top> <DIV><B>內容</B></DIV> </TH> <TH colSpan=2> <DIV> <span><textarea style="WIDTH: 500px;" name="detail" rows="20" cols="90" tabIndex="2" ></textarea></span> </DIV> (不能大於:<FONT color="blue">1000</FONT>字) </TH> </TR> </TABLE> </DIV> <DIV style="MARGIN: 15px 0px; TEXT-ALIGN: center"> <INPUT class="btn" tabIndex="3" type="submit" value="提 交"> <INPUT class="btn" tabIndex="4" type="reset" value="重 置"> </DIV> <s:token/> </FORM>
異常詳細:
java.lang.RuntimeException: Invalid action class configuration that references an unknown class named [postAction]
org.apache.struts2.convention.ConventionsServiceImpl.determineResultPath(ConventionsServiceImpl.java:100)
org.apache.struts2.convention.ConventionUnknownHandler.determinePath(ConventionUnknownHandler.java:385)
org.apache.struts2.convention.ConventionUnknownHandler.handleUnknownResult(ConventionUnknownHandler.java:274)
com.opensymphony.xwork2.DefaultUnknownHandlerManager.handleUnknownResult(DefaultUnknownHandlerManager.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.createResult(DefaultActionInvocation.java:226)
com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:367)
。。。。。。
root cause
java.lang.ClassNotFoundException: postAction
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1680)
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
org.apache.struts2.util.ClassLoaderUtils.loadClass(ClassLoaderUtils.java:111)
org.apache.struts2.convention.ConventionsServiceImpl.determineResultPath(ConventionsServiceImpl.java:98)
org.apache.struts2.convention.ConventionUnknownHandler.determinePath(ConventionUnknownHandler.java:385)
org.apache.struts2.convention.ConventionUnknownHandler.handleUnknownResult(ConventionUnknownHandler.java:274)
com.opensymphony.xwork2.DefaultUnknownHandlerManager.handleUnknownResult(DefaultUnknownHandlerManager.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.createResult(DefaultActionInvocation.java:226)
com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.ja
提示找不到類,一般遇到這個異常,都會認為是spring的配置類名寫錯了或者路徑寫錯了,在網上很多人也說應該是這樣的原因。但在這裡顯示不可能是這種原因。
因為當我實現action介面,即implements Action的時候可以正常執行,則說明肯定不是配置檔案的問題,要不然implements Action的時候就要報錯的。事實上是implements Action的時候正常執行。
這個問題困擾我很久,我在網上查了許許多多有關Action以及ActionSupport的區別,幾乎異口同聲只是說,action只是一個介面,而actionSupport實現了Action介面,並且還提供了許多方法比如效驗等,都建議直接繼承ActionSupport。似乎ActionSupport可以無條件的取代Action介面。我看到這些文章,自然是解決不了我遇到的問題。
後來,我先不管什麼異常,既然繼承了ActionSupport,那 配置一下PostAction-post-validation.xml吧,因為post.jsp只有個標題和正文變數,xml檔案如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="title">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>不能為空!</message>
</field-validator>
<field-validator type="stringlength">
<param name="trim">true</param>
<param name="maxLength">20</param>
<param name="minLength">4</param>
<message>要在4--20位!</message>
</field-validator>
</field>
<field name="detail">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>不能為空!</message>
</field-validator>
<field-validator type="stringlength">
<param name="trim">true</param>
<param name="maxLength">1000</param>
<param name="minLength">4</param>
<message>要在4--1000!</message>
</field-validator>
</field>
</validators>
然後我在strust.xml檔案中,定義 了<result name="input">/error.jsp</result>,其中error.jsp加上:<s:fielderror/><s:actionerror/>
因為ActionSupport實現了ValidationAware介面,可以獲得或設定欄位錯誤資訊、動作錯誤資訊以及動作訊息。其中,hasActionErrors方法判斷是否存在動作層的錯誤,getFieldErrors獲得欄位錯誤資訊(一個Map 物件)。
我的猜測(注意是猜測,我並沒有深入研究):如果某個action繼承了ActionSupport,則ActionSupport裡的方法自然會對相應的jsp檔案進行一些欄位檢測什麼的,如果jsp有問題,則上面的hasActionErrors或getFieldErrors則會返回true,這時根據struts.xml返回“input”定義的檢視頁面。
例如,我上面在post.jsp中點擊發帖按鈕,程式便應該執行postAction,但執行之前會經過攔截器,以及ActionSupport中的校驗(我在這裡的測試結果顯示,當點擊發帖按鈕,執行順序是:過濾器---->攔截器--->ActionSupport的效驗,其中過濾器web.xml的位置和url-pattern的路徑設定有講究,如果過濾器放在struts之後,結果大不一樣,但攔截器-->ActionSupport校驗這個順序沒變),如果定義了效驗xml檔案以及“input”對應的jsp檔案,如這時我在實際測試過程發現頁面調轉到了error.jsp。如下:
當時我看到這個介面真是太興奮太高興了。終於知道程式的問題在哪裡。我於是找到post.jsp檔案,找到有關boardId欄位的資訊,發現下面這句
<INPUT id="a3" type="hidden" name="boardId" value=<%= session.getAttribute("boardId")%>/>屬性值寫錯了,action中“session.put("BOARD_ID", boardId);
所以這句程式碼修改為:
<INPUT id="a3" type="hidden" name="boardId" value=<%= session.getAttribute("BOARD_ID")%>/>
再次執行,終於一切正常了!!看來,<s:fielderror/><s:actionerror/>用處真大呀。由此看出,繼承ActionSupport還有個好處,就是它會幫你堅持jsp中每個欄位屬性,是否有效,如上面的截圖,當遇到無效欄位時,就直接轉入input,不再執行action,從而報出找不到類的異常。而實現Action介面,則不會對jsp檔案進行欄位檢測,所以雖然我的jsp有錯誤的時候,也一樣可以進入action執行。
其實對jsp的相關知識自己也需要加強,例如這次出現的許多多此一舉的事情。其中我需要在jsp中擷取一個字串,需要字串的前20個字元就好,為了這個,我居然用了一段js程式碼擷取,如下:
function SetString(str,len)
{
var strlen = 0;
var s = "";
for(var i = 0;i < str.length;i++)
{
if(str.charCodeAt(i) > 128){
strlen += 2;
}else{
strlen++;
}
s += str.charAt(i);
if(strlen >= len){
return s ;
}
}
return s;
}
<script>
document.write(SetString("<%=session.getAttribute("BOARD_NAME").toString().trim()%>",20));
</script>
寫了這2段程式碼來實現,可是後來學習了jsp標籤,真笑話自己啊。上面的程式碼在jsp檔案其實只需要2句即可,如下:
<c:set var="bName" value="${session.BOARD_NAME}"/>
${fn:substring(bName,0,20)}
當然需要引用
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
不過這裡我還得提出一個問題,如果上面我用struts2的標籤函式substring擷取字串,遇到中文的時候我這裡是無法擷取的,當時顯示的頁面上,遇到中文字串就是空白,英文字串就可以正常擷取。不知道是出了什麼問題,這些留待以後研究了。
到這裡,這次的總結算是寫完了,感覺寫學習總結學習到的似乎不比寫程式學習到的少。希望各位朋友也多寫總結,利人利己,呵呵。