1. 程式人生 > >學習struts2建bbs總結七:extends ActionSupport帶來的ClassNotFoundException異常

學習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擷取字串,遇到中文的時候我這裡是無法擷取的,當時顯示的頁面上,遇到中文字串就是空白,英文字串就可以正常擷取。不知道是出了什麼問題,這些留待以後研究了。

到這裡,這次的總結算是寫完了,感覺寫學習總結學習到的似乎不比寫程式學習到的少。希望各位朋友也多寫總結,利人利己,呵呵。