下面是我们项目的异常处理机制,在此记录一下。
一、自定义异常
首先,我们自定义了两种类型的异常:DataWarningException和ServerErrorException
DataWarningException.java
package com.augmentum.ot.exception;
public class DataWarningException extends Exception {
private static final long serialVersionUID = -1462714485890122558L;
public DataWarningException(String message, Throwable cause) {
super(message, cause);
}
public DataWarningException(String message) {
super(message);
}
public DataWarningException(Throwable cause) {
super(cause);
}
}
ServerErrorException.java
package com.augmentum.ot.exception;
public class ServerErrorException extends Exception {
private static final long serialVersionUID = -1462714485890122558L;
public ServerErrorException(String message, Throwable cause) {
super(message, cause);
}
public ServerErrorException(String message) {
super(message);
}
public ServerErrorException(Throwable cause) {
super(cause);
}
}
其中,当发生数据异常,比如说操作数据库失败,数据验证错误,在service抛出ServerErrorException;当出现业务逻辑错误,在service抛出 DataWarningException.
二、定义并初始化异常信息
在自定义完异常后,我们有创建了一个Exception.xml文件来记录异常信息,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE PUBLIC>
<!-- 其中errorMessageKey表示的是传给前台的错误信息;
logMessage表示的是当异常发生时打印的log信息;
flag表示的是是否要跳页面,如果flag=1表示要跳页面,flag=0表示不跳页面 -->
<exception>
<!-- Server internal error -->
<errorCode id="E0001">
<errorMessageKey></errorMessageKey>
<logMessage>Exception error.</logMessage>
<flag>1</flag>
</errorCode>
<errorCode id="E0002">
<errorMessageKey></errorMessageKey>
<logMessage>Validation failure.</logMessage>
<flag>1</flag>
</errorCode>
<errorCode id="E0005">
<logMessage>Employee [#_employee_id] has no access to [actionName].</logMessage>
<errorMessageKey>_no_privilege</errorMessageKey>
<flag>1</flag>
</errorCode>
......
<!-- Common message -->
<errorCode id="W0001">
<logMessage>There is no data</logMessage>
<errorMessageKey>no_data</errorMessageKey>
<flag>0</flag>
</errorCode>
<!-- Course part exception -->
<errorCode id="W1001">
<logMessage>Failed to view detail course [#_course_id] that it is already deleted.</logMessage>
<errorMessageKey>_course_not_exist_error</errorMessageKey>
<flag>1</flag>
</errorCode>
</exception>
创建一个和其对应的DTO类存放每个异常信息
ErrorCode.java
public class ErrorCode {
private String errorCodeId;
private String flag;
private String errorMessage;
private String logMessage;
public String getLogMessage() {
return logMessage;
}
public void setLogMessage(String logMessage) {
this.logMessage = logMessage;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getErrorCodeId() {
return errorCodeId;
}
public void setErrorCodeId(String errorCodeId) {
this.errorCodeId = errorCodeId;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
定义一个常量类存放ErrorCodeId
ErrorCodeConstants.java
public class ErrorCodeConstants {
// Common Part
public static final String SERVER_ERROR = "E0001";
public static final String SERVER_VALIDATION_ERROR = "E0002";
public static final String ERROR = "E0004";
public static final String NO_ACCESS = "E0005";
......
}
在启动项目的时候,写一个Servlet解析这些异常信息,并将其放入ServletContext中。
InitDataServlet.java
package com.augmentum.ot.initServlet;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.springframework.stereotype.Component;
import com.augmentum.ot.dataObject.ErrorCode;
import com.augmentum.ot.dataObject.constant.FlagConstants;
import com.augmentum.ot.util.ReaderXmlUtils;
@Component
public class InitDataServlet extends HttpServlet {
private static final long serialVersionUID = 2705746245967369017L;
@Override
public void init() throws ServletException {
super.init();
ServletContext servletContext = getServletContext();
try {
Map<String, ErrorCode> errorCodeMap = ReaderXmlUtils.getErrorCodes(); //自定义的解析xml的工具类
servletContext.setAttribute(FlagConstants.ERROR_CODE_MAP, errorCodeMap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
web.xml
<servlet>
<display-name>InitDataServlet</display-name>
<servlet-name>InitDataServlet</servlet-name>
<servlet-class>com.augmentum.ot.initServlet.InitDataServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>InitDataServlet</servlet-name>
<url-pattern>/InitDataServlet</url-pattern>
</servlet-mapping>
三、在service层和action层抛出异常
下面是service方法的一个方法
@Override
public Course getDetailCourseById(Integer id) throws DataWarningException, ServerErrorException {
logger.debug(LogConstants.getDebugInput("courseId", id));
if (id == null || id <= 0) {
logger.error(LogConstants.objectIsNULLOrEmpty("id"));
throw new ServerErrorException(ErrorCodeConstants.SERVER_VALIDATION_ERROR);
}
Course course;
try {
course = courseDao.findByPrimaryKey(id);
} catch (Exception e) {
logger.error(e.getStackTrace(), e);
throw new ServerErrorException(ErrorCodeConstants.SERVER_ERROR);
}
if (course == null || course.getCourseIsDeleted() == FlagConstants.IS_DELETED) {
logger.warn(LogConstants.getLogCourseByErrorCode(ErrorCodeConstants.VIEW_COURSE_DELETED, id));
throw new DataWarningException(ErrorCodeConstants.VIEW_COURSE_DELETED);
}
......
logger.debug(LogConstants.getDebugOutput("Course", course.toString()));
return course;
}
下面是action层的一个方法
public String viewCourseDetail() throws DataWarningException, ServerErrorException {
......
course = courseService.getDetailCourseById(courseId);
......
return SUCCESS;
}
四、在Interceptor中处理异常
在我们系统中,每个Action类都继承一个BaseAction,在BaseAction中有个JSONObject对象,存放返回给前台的数据。所以错误数据我们可以存放到jsonObject中
ExceptionInterceptor.java
public class ExceptionInterceptor extends ExceptionMappingInterceptor {
private static final long serialVersionUID = -3837674457299306885L;
private static final Logger logger = Logger.getLogger(ExceptionInterceptor.class);
private BaseAction baseAction;
private boolean isAjaxRequest(HttpServletRequest request) {
String header = request.getHeader("X-Requested-With");
if (header != null && "XMLHttpRequest".equals(header))
return true;
else
return false;
}
private String handleExceptionByServerErrorException(Exception e, boolean isAjax) {
String errorCode = e.getMessage();
if (null == errorCode || "".equals(errorCode)) {
errorCode = ErrorCodeConstants.SERVER_ERROR;
}
ServletContext context = ServletActionContext.getServletContext();
@SuppressWarnings("unchecked")
Map<String, ErrorCode> errorCodeMap = (Map<String, ErrorCode>) context.getAttribute(FlagConstants.ERROR_CODE_MAP);
ErrorCode errorCodeEntity = errorCodeMap.get(errorCode);
logger.error(errorCodeEntity.getErrorMessage());
if (isAjax) {
// Make the JSON object.
baseAction.setJsonObject(JSONObject.fromObject(errorCodeEntity));
return FlagConstants.ERROR_JSON; // "error_json"
} else {
baseAction.setErrorCode(errorCodeEntity);
return FlagConstants.ERROR_PAGE; //"error_page"
}
}
private String handleDataWarningException(Exception e, boolean isAjax) {
String errorCode = e.getMessage();
ServletContext context = ServletActionContext.getServletContext();
@SuppressWarnings("unchecked")
Map<String, ErrorCode> errorCodeMap = (Map<String, ErrorCode>) context.getAttribute(FlagConstants.ERROR_CODE_MAP);
ErrorCode errorCodeEntity = errorCodeMap.get(errorCode);
errorCodeEntity.setErrorMessage(baseAction.getText(errorCodeEntity.getErrorMessageKey()));
if (isAjax == true) {
baseAction.setJsonObject(JSONObject.fromObject(errorCodeEntity));
return FlagConstants.ERROR_JSON;
} else {
baseAction.setErrorCode(errorCodeEntity);
return FlagConstants.ERROR_PAGE;
}
}
private String handleException(boolean isAjax) {
String errorCode = ErrorCodeConstants.SERVER_ERROR;
ServletContext context = ServletActionContext.getServletContext();
@SuppressWarnings("unchecked")
Map<String, ErrorCode> errorCodeMap = (Map<String, ErrorCode>) context.getAttribute(FlagConstants.ERROR_CODE_MAP);
ErrorCode errorCodeEntity = errorCodeMap.get(errorCode);
if (isAjax) {
// Make the JSON object.
baseAction.setJsonObject(JSONObject.fromObject(errorCodeEntity));
return FlagConstants.ERROR_JSON;
} else {
baseAction.setErrorCode(errorCodeEntity);
return FlagConstants.ERROR_PAGE;
}
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
try {
return invocation.invoke();
} catch (Exception e) {
baseAction = (BaseAction) invocation.getAction();
HttpServletRequest request = ServletActionContext.getRequest();
boolean isAjax = isAjaxRequest(request);
if (e instanceof ServerErrorException) {
return handleExceptionByServerErrorException(e, isAjax);
} else if (e instanceof DataWarningException) {
return handleDataWarningException(e, isAjax);
} else {
logger.error(e.getStackTrace(), e);
return handleException(isAjax);
}
}
}
}
struts.xml
<global-results>
<result name="error_page" type="chain">
<param name="actionName">handleError</param>
<param name="namespace">/error</param>
</result>
<result name="error_json" type="json">
<param name="root">jsonObject</param>
</result>
</global-results>
其中handleError 是用于非ajax时的页面跳转的Action
五、前台处理
/*
data: 前台返回的数据
customUrl: 当发生异常要跳转页面时页面的URL
finalHandleFunc:弹出对话框后点击对话框后执行的函数
finalHandleFuncParam:上面函数所需参数
*/
function handleException(data, customUrl, finalHandleFunc, finalHandleFuncParam) {
if (data && data.errorCodeId) {
// 不跳转页面
if (data.flag == "0") {
// 先弹出对话框提示错误信息,当点击OK按钮时执行finalHandleFunc函数
initialErrorMsgBar(data.errorMessage, function() {
if(finalHandleFunc) {
finalHandleFunc(finalHandleFuncParam);
}
});
}
// 跳转页面
if (data.flag == "1") {
if (data.errorCodeId == "E0001" || data.errorCodeId == "E0002") {
// 特殊情况,如无权限情况
window.onbeforeunload = null;
window.location.href = $("#basePath").val() + "error/handleError?errorCode.errorCodeId=" + data.errorCodeId;
} else if(customUrl){
// 自定义跳转页面时,先弹出对话框提示错误信息,在跳错误页面
initialErrorMsgBar(data.errorMessage, function() {
window.onbeforeunload = null;
window.location = customUrl;
});
} else {
// 先弹出对话框提示错误信息,再跳到系统默认页面
initialErrorMsgBar(data.errorMessage, function() {
window.onbeforeunload = null;
window.location = $('#basePath').val() + 'dashboard/dashboard_dashboard';
});
}
}
return false;
}
return true;
}