您的位置:首页 > 编程语言 > Java开发

Struts中的异常处理

2011-03-05 14:45 323 查看
代码说话,是最有效的证明,请看。。

首先是web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
					 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 	<servlet>
		<servlet-name>action</servlet-name>
		<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
		<init-param>
			<param-name>config</param-name>
			<param-value>/WEB-INF/struts-config.xml</param-value>
		</init-param>
		<init-param>
			<param-name>debug</param-name>
			<param-value>3</param-value>
		</init-param>
		<init-param>
			<param-name>detail</param-name>
			<param-value>3</param-value>
		</init-param>
		<load-on-startup>0</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>action</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	
	<welcome-file-list>
		<welcome-file>login.jsp</welcome-file>
	</welcome-file-list>
</web-app>


然后是用于激活各种异常的用户登录页面login.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="html" uri="http://struts.apache.org/tags-html" %>
<h2>演示Struts的异常处理</h2>
<html:errors/>
<h4>
	<font color="red">提示:</font>程序设定的用户名和密码分别为
	<font color="blue"><strong>admin</strong></font>和
	<font color="blue"><strong>jadyer</strong></font>
</h4>
<form action="login.do" method="POST">
	用户:<input type="text" name="username"><br/>
	密码:<input type="password" name="password"><br/>
	<input type="submit" value="登录">
</form>
<%--
==============================================================================================
1、编程式异常
	* 截获异常
	* 创建相应的异常消息
	* 传递异常消息
	* 转向相应的页面处理异常
==============================================================================================
2、声明式异常(自动处理的异常)
	* 在struts-config.xml文件中配置<exeception/>标签
	* 理解局部和全局exception
	* 注意局部<exception/>标签需要配置到<forward/>标签的前面,详见DTD中的约束
==============================================================================================
3、<exeception/>标签中的属性说明:
	* key:指异常信息对应的国际化消息文本,这个key值需要在国际化资源文件中定义
	* type: 处理哪种异常
	* path: 定义一但出现异常,需要转向页面
	        若不定义path,默认将使用<action>标签中input属性对应的页面
	* scope:可以取值request和session,默认为request
	* handler:异常的处理类,struts默认采用org.apache.struts.action.ExceptionHandler
	           如果做个性化的异常处理可以继承此类覆写相应的方法
==============================================================================================
4、参见:ErrorCodeExceptionHandler.java和AppExceptionHandler.java
==============================================================================================
--%>


用户名和密码均正确时的登录成功页面login_success.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="html" uri="http://struts.apache.org/tags-html" %>
<%-- 请将struts-html.tld文件放置在WEB-INF目录下 --%>
<html:errors/>


然后是Struts1.x的核心配置文件struts-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://struts.apache.org/dtds/struts-config_1_2.dtd">
<struts-config>
	<form-beans>
		<form-bean name="loginForm" type="com.jadyer.struts.form.LoginActionForm"/>
	</form-beans>
	<global-exceptions>
		<exception key="error.exception" type="com.jadyer.custom.AppException"/>
	</global-exceptions>
	<action-mappings>
		<action path="/login"
				type="com.jadyer.struts.action.LoginAction"
				name="loginForm"
				scope="request"
				validate="false"
				input="/login.jsp">
			<forward name="success" path="/login_success.jsp"/>
		</action>
	</action-mappings>
	<message-resources parameter="res.MessageResources" />
</struts-config>

<!-- *********************【演示Struts的声明式异常时,所用到的异常类】******************************************************************* -->
<!-- <exception key="user.not.found" type="com.jadyer.struts.UserNotFoundException" path="/login_error.jsp"/> -->
<!-- <exception key="user.password.error" type="com.jadyer.struts.PasswordErrorException" path="/login_error.jsp"/> -->
<!-- <exception/>中key属性指异常信息对应的国际化消息文本,这个key值需要在国际化资源文件中定义,type属性指定处理的具体的哪种异常 -->
<!-- path属性定义一旦出现异常,需要转向哪个页面。如果不定义path,默认情况下将转向到<action/>标签中input属性对应的页面 -->
<!-- 也就是说,当发生UserNotFoundException异常的时候,就显示国际化资源文件中的user.not.found所对应的国际化提示信息 -->
<!-- 由于此时所出现的异常,也被Struts设到ERROR_KEY上了,所以在页面中便可通过<html:errors/>或者<html:messages/>获取异常 -->
<!-- 但是,如果同时设定的input和path属性,那么path的优先级要高一些,异常信息会优先转向到path属性所指向的页面 -->
<!-- <exception/>和<forward/>类似,它也有一个全局的概念 -->
<!-- 所以,这种异常通常被定义成全局的。这样一来,只要Action发生这种异常,那么它就会自动按照全局异常中的设置进行相应的转向或处理 -->
<!-- 如果全局异常中没有指定path值,那么当发生全局异常时,会自动转向到各自的<action/>标签中所设定的input对应的页面 -->
<!-- *********************【演示Struts的个性化异常时,所用到的异常类==》此时用到了国际化资源文件】******************************************* -->
<!-- <exception key="error.code" type="com.jadyer.custom.ErrorCodeException" handler="com.jadyer.custom.ErrorCodeExceptionHandler"/> -->
<!-- 此时key值可随意,如error.code或error.exception等等。因为UserManager中throw new ErrorCodeException("user.password.error")已指定Key值 -->
<!-- *********************【演示Struts的个性化异常时,所用到的异常类==》此时没有用到国际化资源文件】****************************************** -->
<!-- <exception key="error.exception" type="com.jadyer.custom.AppException" handler="com.jadyer.custom.AppExceptionHandler"/> -->
<!-- 其实这里指定的AppExceptionHandler.java的处理过程与Struts默认处理过程是一样的,故可省略掉handler="com.jadyer.custom.AppExceptionHandler" -->
<!-- 我们查看了Struts源码中的ExceptionHandler.class的第98行,即error = new ActionMessage(ae.getKey(), ex.getMessage()); -->
<!-- 可见Struts的异常处理与国际化消息文本结合了,即它肯定会new ActionMessage()出来,而且ActionMessage()里面必须得有ae.getKey(),即国际化消息文本 -->
<!-- 而我们的UserManager.java中的throw new AppException("密码不正确!")根本就没有传递国际化key值,这里传的只是一个普通文本,那怎么办呢? -->
<!-- 事实上,关于这个Key值,我们只要在国际化资源文件中给一个就可以了,所以我们把<exception key="">里面的error.exception配置到国际化资源文件中 -->
<!-- 而在国际化资源文件中,我们应该给error.exception一个什么值呢?其实给error.exception任何值都是没有意义的,所以我们给它一个占位符{0}就可以了 -->
<!-- 即error.exception={0}。而我们只需要填充这个占位符即可。比如throw new AppException("密码不正确!");那么此时"密码不正确!"便传到{0}上了 -->
<!-- ****************************************************************************************************************************** -->


用于收集前台用户名和密码信息的ActionForm类

package com.jadyer.struts.form;

import org.apache.struts.action.ActionForm;

@SuppressWarnings("serial")
public class LoginActionForm extends ActionForm {
	private String username;
	private String password;
	/*--两个属性的setter和getter略--*/
}


根据用户名和密码的不同进行分发处理的Action类

package com.jadyer.struts.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

import com.jadyer.struts.UserManager;
import com.jadyer.struts.form.LoginActionForm;

public class LoginAction extends Action {
	@Override
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		LoginActionForm laf = (LoginActionForm)form;
		String username = laf.getUsername();
		String password = laf.getPassword();
		
		UserManager.getInstance().login(username, password);
		
		ActionMessages messages = new ActionMessages();
		ActionMessage message = new ActionMessage("user.login.success",username);
		messages.add("loginSuccess", message);
		
		//this.saveMessages(request, messages); //request.setAttribute(Globals.MESSAGE_KEY,messages);
		this.saveErrors(request, messages); //request.setAttribute(Globals.ERROR_KEY,errors);
		
		return mapping.findForward("success");
	}
}
//-------------------------------------------------------------------------------------------------------
//【Struts的声明式异常】
//由于在LoginAction中没有try{}catch{}异常,那么当调用login(username,password)方法后
//可能就会出现在UserManager中设定的两个异常
//当出现这两个异常的时候,就会自动向上抛,它会抛到LoginAction中
//然后发现在LoginAction中没有对其进行拦截,那么它会继续向上抛,然后就会抛到Struts框架中
//-------------------------------------------------------------------------------------------------------
//接着就会在struts-config.xml中查找是否对该异常进行的<exception/>的设置
//如果没有发现对该异常的配置,那么就会对该异常进行判断
//若该异常是IOException,则转换成IOException,若该异常是ServletException,则转换成ServletException
//否则就继续向上抛。也就是说,Struts框架就不再处理了,然后就会抛到Web容器里
//如果在web.xml文件中对该异常进行了设置,那么就会按照web.xml中对该异常的处理意图进行处理
//如果在web.xml文件中也没有对该异常进行配置,那么这个异常将会直接显示在浏览器页面上
//-------------------------------------------------------------------------------------------------------
//若在struts-config.xml中查找到对该异常的配置,那么Struts框架会接手处理该异常,处理过程如下
//Struts首先会创建ExceptionHandler类,然后调用该类的excute()方法,最后返回一个ActionForward
//具体处理过程可以查看RequestProcessor.class中的process()方法
//然后通过process()方法就可以查看processActionPerform()方法
//接着就会发现processActionPerform()调用了processException()方法来处理异常
//然后可以在processException()方法中发现又调用了execute()方法
//接着再F3查看execute()方法代码,分析具体的异常处理方式。总之,还是多研究研究Struts的源码吧......
//-------------------------------------------------------------------------------------------------------
//补充一下,ExceptionHandler类是Struts的默认处理异常的类
//对于某些异常的处理而言,Struts是满足不了我们的要求的,这时就需要个性化处理
//也就是说我们要扩展Struts,那么此时就需要扩展ExceptionHandler类
//-------------------------------------------------------------------------------------------------------


然后是用于验证用户名和密码正确与否的UserManager.java

package com.jadyer.struts;

import com.jadyer.custom.AppException;

public class UserManager {
	private static UserManager instance = new UserManager();
	
	private UserManager() {}
	
	public static UserManager getInstance() {
		return instance;
	}
	
//	/**
//	 * 演示Struts的声明式异常时,所用到的方法
//	 */
//	public void login(String username, String password) {
//		if (!"admin".equals(username)) {
//			throw new UserNotFoundException(username);
//		}
//		//为了将username传递到国际化的user.not.found里的占位符中
//		//所以在UserNotFoundException.java中添加一个构造方法
//		if (!"jadyer".equals(password)) {
//			throw new PasswordErrorException();
//		}
//	}

//	/**
//	 * 演示Struts的个性化异常时,所用到的方法
//	 * 这里用到了国际化资源文件中的Key
//	 */
//	public void login(String username, String password) {
//		if (!"admin".equals(username)) {
//			throw new ErrorCodeException("user.not.found", username);
//		}
//		//在这里抛出的所有的异常信息,必须要关联一条国际化消息
//		//但是项目有可能没有必要做国际化。具体的实现详见AppException
//		if (!"jadyer".equals(password)) {
//			throw new ErrorCodeException("user.password.error");
//		}
//	}

	/**
	 * 演示Struts的个性化异常时,所用到的方法
	 * 这里没有用到国际化资源文件中的Key
	 */
	public void login(String username, String password) {
		if (!"admin".equals(username)) {
			throw new AppException("用户不能找到,用户=【" + username + "】");
		}
		if (!"jadyer".equals(password)) {
			throw new AppException("密码不正确!");
		}
	}
}


用于演示Struts1.x中的声明式异常处理的两个自定义异常类

package com.jadyer.struts;
@SuppressWarnings("serial")
public class UserNotFoundException extends RuntimeException {
	public UserNotFoundException(String msg) {
		super(msg); //只适合有一个占位符的情况下
	}
}

/**
 * 用于演示Struts1.x中的声明式异常处理的两个自定义异常类
 * 上面的是:当用户名不存在时,触发的异常
 * 下面的是:当密码输入错误时,触发的异常
 */

package com.jadyer.struts;
@SuppressWarnings("serial")
public class PasswordErrorException extends RuntimeException {}


用于演示Struts1.x中的个性化异常处理的两个自定义异常类



package com.jadyer.custom;
@SuppressWarnings("serial")
public class AppException extends RuntimeException {
	public AppException(String msg) {
		super(msg);
	}
}

/**
 * 用于演示Struts1.x中的个性化异常处理的两个自定义异常类
 * 上面的自定义的个性化异常类,在使用的过程中,没有用到国际化资源文件中的Key
 * 下面...................,............,使用到了国际化资源文件中的Key
 */

package com.jadyer.custom;
/**
 * 在这个类中放入错误码
 * @see --------------------------------------------------------------------------------
 * @see 为了在处理的时候能够方便一些,不用显式的声明和截取
 * @see 所以把ErrorCodeException声明成RuntimeException,即运行期异常
 * @see --------------------------------------------------------------------------------
 * @see 为了处理起来方便,所以错误码的值特别建议与国际化消息文本的key相同
 * @see 因为当我们抛出该异常之后,Struts框架会拿到这个错误码
 * @see 然后它直接会New一个ActionMessage,于是就拿到了对应的国际化消息文本了
 * @see 如果我们抛出去一个123,那么还要根据123来转换
 * @see 错误码和国际化消息一样的话,就能省略转换,方便很多
 * @see --------------------------------------------------------------------------------
 */
@SuppressWarnings("serial")
public class ErrorCodeException extends RuntimeException {
	//由于错误码与国际化的key相同,所以就会获取到key对应的国际化消息文本
	//可能该key对应的国际化文本中有不确定个数的占位符
	//所以就需要添充占位符,所以就需要增加一个对象数组
	//对象数组的第0个元素就添充到第0个占位符上,第1个添充到第1个
	private Object[] args;
	private String errorCode;
	/**
	 * 国际化消息文本中没有占位符
	 */
	public ErrorCodeException(String errorCode) {
		this(errorCode, null);
	}
	/**
	 * 国际化消息文本中有一个占位符
	 */
	public ErrorCodeException(String errorCode, Object args0) {
		this(errorCode, new Object[]{args0});
	}
	/**
	 * 国际化消息文本中有不确定个数的占位符
	 */
	public ErrorCodeException(String errorCode, Object[] args) {
		this.errorCode = errorCode;
		this.args = args;
	}
	//只为它们提供get方法。至于赋值,则通过构造方法
	//这样,通过这个异常类,就能拿到它的参数和错误码
	//这个错误码,在这里就是我们的国际化消息文本
	public String getErrorCode() {
		return errorCode;
	}
	public Object[] getArgs() {
		return args;
	}
}


关于自定义的未使用到国际化资源文件中Key的AppException异常的处理类

package com.jadyer.custom;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ExceptionHandler;
import org.apache.struts.config.ExceptionConfig;
import org.apache.struts.util.ModuleException;

/**
 * 其实我们不用写这个处理类
 * 因为它和默认处理是一样的
 * 它的默认处理就能够处理了
 * 写这个类的目的是了解了解
 */
public class AppExceptionHandler extends ExceptionHandler {
	public ActionForward execute(Exception ex, ExceptionConfig ae,
			ActionMapping mapping, ActionForm formInstance,
			HttpServletRequest request, HttpServletResponse response)
			throws ServletException {

		if (!(ex instanceof AppException)) {
			return super.execute(ex, ae, mapping, formInstance, request, response);
		}

		ActionForward forward = null;
		ActionMessage error = null;
		String property = null;

		// Build the forward from the exception mapping if it exists or from the form input
		if (ae.getPath() != null) {
			forward = new ActionForward(ae.getPath());
		} else {
			forward = mapping.getInputForward();
		}

		// Figure out the error
		if (ex instanceof ModuleException) {
			error = ((ModuleException) ex).getActionMessage();
			property = ((ModuleException) ex).getProperty();
		} else {
			AppException appe = (AppException) ex;
			error = new ActionMessage(ae.getKey(), appe.getMessage());
			property = error.getKey();
			// error = new ActionMessage(ae.getKey(), ex.getMessage());
			// property = error.getKey();
		}

		this.logException(ex);

		// Store the exception
		request.setAttribute(Globals.EXCEPTION_KEY, ex);
		this.storeException(request, property, error, forward, ae.getScope());

		return forward;
	}
}


关于自定义的使用到了国际化资源文件中key的ErrorCodeException异常的处理类

package com.jadyer.custom;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ExceptionHandler;
import org.apache.struts.config.ExceptionConfig;
import org.apache.struts.util.ModuleException;

/**
 * 个性化异常处理类
 * 该类只处理ErrorCodeException异常
 */
public class ErrorCodeExceptionHandler extends ExceptionHandler {
	// 复写ExceptionHandler类的execute()方法
	public ActionForward execute(Exception ex, ExceptionConfig ae,
			ActionMapping mapping, ActionForm formInstance,
			HttpServletRequest request, HttpServletResponse response)
			throws ServletException {
		
		// 容错处理。注意需要return返回ActionForward,否则程序还会继续向下执行的
		// 即如果该异常不是ErrorCodeException异常,那么我们就不再处理该异常了,而是转交给Struts的默认异常处理类
		if (!(ex instanceof ErrorCodeException)) {
			return super.execute(ex, ae, mapping, formInstance, request, response);
		}

		ActionForward forward = null;
		ActionMessage error = null;
		String property = null;

		// Build the forward from the exception mapping if it exists or from the form input
		// ae即参数中的ExceptionConfig,即代表struts-config.xml中的<exception/>标签中的配置信息
		if (ae.getPath() != null) {
			forward = new ActionForward(ae.getPath());
		} else {
			forward = mapping.getInputForward();
		}

		// Figure out the error
		if (ex instanceof ModuleException) {
			error = ((ModuleException) ex).getActionMessage();
			property = ((ModuleException) ex).getProperty();
		} else {
			// 通过UserManager中的throw new ErrorCodeException("user.not.found",username)
			// 说明我们通过ErrorCodeException已经把errorCode和它的占位符携带过来了
			// 所以在我们定义的异常处理类中就可以获取到errorCode和它的占位符
			// 由于我们传入的是国际化消息文本中的key,所以此时其实已经拿到了国际化消息文本
			ErrorCodeException ece = (ErrorCodeException) ex;
			String errorCode = ece.getErrorCode();
			Object[] args = ece.getArgs();
			
			// error = new ActionMessage(ae.getKey(), ex.getMessage());
			// property = error.getKey();
			// 这是Struts的ExceptionHandler类的默认处理
			// 但它已经满足不了我们的要求了,所以要把它注释掉,然后手工定义我们的处理方式
			error = new ActionMessage(errorCode, args);
			property = error.getKey();
		}
		
		this.logException(ex);

		// Store the exception
		request.setAttribute(Globals.EXCEPTION_KEY, ex);
		this.storeException(request, property, error, forward, ae.getScope());

		return forward;
	}
}


最后是使用到的国际化资源文件MessageResources.properties

#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
# -- standard errors --

error.exception = {0}

errors.footer = </UL>
errors.header = <UL>
errors.prefix = <LI><font color="red"><strong>
errors.suffix = </strong></font></LI>

user.login.success  = {0},Login Success
user.not.found      = User Not Found,UserName=[{0}]
user.password.error = Password is Error

#下面是MessageResources_zh_CN.properties文件的内容

error.exception = {0}

errors.footer = </UL>
errors.header = <UL>
errors.prefix = <LI><font color="red"><strong>
errors.suffix = </strong></font></LI>

user.login.success  = {0},/u767B/u5F55/u6210/u529F
user.not.found      = /u7528/u6237/u4E0D/u80FD/u627E/u5230/uFF0C/u7528/u6237/u540D/u79F0/=[{0}]
user.password.error = /u5BC6/u7801/u9519/u8BEF
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: