實際開發過程中會遇到對dao層、service層和controller層拋出的異常統一處理,對每個模塊的方法進行異常處理也能解決問題,但這樣是不合理的,並且代碼看起來也不雅觀,這時就可以使用@ExceptionHandler對Web層進行統一異常處理,讓異常由下往上(dao—>service—>controller)拋出。代碼如下:
import com.sand.base.core.common.BaseCommon; import com.sand.base.core.entity.ResultEntity; import com.sand.base.enums.ResultEnum; import com.sand.base.exception.LsException; import com.sand.base.util.ResultUtil; import com.sand.base.util.editor.DateEditor; import com.sand.base.util.editor.StringEditor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.Date; import java.util.Objects; /** * 功能說明:Web層通用數據處理 * 開發人員:@author liusha * 開發日期:2019/8/26 21:43 * 功能描述:定義所有控制器的父控制器,進行屬性綁定、數據轉換、異常處理 */ @Slf4j public class BaseController extends BaseCommon { protected HttpSession session; protected HttpServletRequest request; protected HttpServletResponse response; /** * 屬性訪問器 * * @param session session * @param request request * @param response response */ @ModelAttribute public void modelAttribute(HttpSession session, HttpServletRequest request, HttpServletResponse response) { this.session = session; this.request = request; this.response = response; } /** * 屬性編輯器 * * @param binder 數據綁定 */ @InitBinder public void initBinder(WebDataBinder binder) { // Date類型轉換,幾乎支持所有的日期類型 binder.registerCustomEditor(Date.class, new DateEditor()); // String類型轉換,將所有傳遞進來的String進行HTML編碼,防止XSS攻擊 binder.registerCustomEditor(String.class, new StringEditor()); } /** * 頂級異常處理 * * @param e 異常 * @return 響應客戶端 */ @ExceptionHandler(Exception.class) public ResultEntity handleException(Exception e) { errorLog(e); return ResultUtil.error(); } /** * 自定義異常處理 * * @param e 異常 * @return 響應客戶端 */ @ExceptionHandler(LsException.class) public ResultEntity handleLsException(LsException e) { errorLog(e); return ResultUtil.info(e.getCode(), e.getMessage()); } /** * 參數缺失處理 * * @param e 異常 * @return 響應客戶端 */ @ExceptionHandler(MissingServletRequestParameterException.class) public ResultEntity handleMissingParamException(MissingServletRequestParameterException e) { errorLog(e); return ResultUtil.error(ResultEnum.PARAM_MISSING_ERROR); } /** * 讀取方法處理 * * @param e 異常 * @return 響應客戶端 */ @ExceptionHandler(HttpMessageNotReadableException.class) public ResultEntity handleMessageNotReadableException(HttpMessageNotReadableException e) { errorLog(e); return ResultUtil.error(ResultEnum.READ_METHOD_ERROR); } /** * 打印出錯log * * @param e 異常 */ private void errorLog(Exception e) { StackTraceElement element = e.getStackTrace()[0]; if (e instanceof LsException) { log.info("異常位置:{}.{},第{}行,原因:{}", element.getClassName(), element.getMethodName(), element.getLineNumber(), e.getMessage()); if (!Objects.isNull(e.getCause())) { StackTraceElement cause = e.getCause().getStackTrace()[0]; log.info("起因:{}.{},第{}行,原因:{}", cause.getClassName(), cause.getMethodName(), cause.getLineNumber(), e.getCause().getMessage()); } } else { log.error("錯誤位置:{}.{},第{}行,錯誤原因:{}", element.getClassName(), element.getMethodName(), element.getLineNumber(), e.getClass().getName()); } log.error("錯誤信息:" + e); } }
需要注意的是,這種處理方式只對當前controller有效,也就是只有當controller繼承了BaseController才能捕獲到異常。如果希望沒有繼承BaseController的controller也能進行異常捕獲,就需要新建一個@ControllerAdvice切面類配合@ExceptionHandler進行全局異常處理(值得注意的是,其它所有的切面類需將異常拋出而不是捕獲處理,異常處理流程由統一異常處理類去完成,方便排查問題)。
我這里只列出了其中幾種異常,異常種類遠遠不止這些,可以參考API文檔進行選擇自己需要的異常http://tool.oschina.net/apidocs/apidoc?api=Spring-3.1.1