Spring MVC 統一異常處理的兩種方式
沒有廢話,直接來。
方式一
通過@ControllerAdvice 和 @ExceptionHandler 方法。
@ControllerAdvice 這個註解,可以將對於控制器的全域性配置放到註解了@ControllerAdvice的類上,它結合了 @Component 所以可以自動註冊為bean
註解了@Controller的類的方法可使用 @ExceptionHandler @InitBinder @MoudelAttribute
註解到方法上
以上這些對所有註解了@requestMapping的方法有效
@ExceptionHandler 用於全域性處理控制器的異常
@InitBinder 用來設定WebDataBinder ,WebDataBinder 用來自動繫結前臺請求引數到Model
@MoudelAttribute 作用是繫結鍵值對到Model中,此處是讓全域性的@RequestMapping都能獲取到此處設定的鍵值對。
看到這裡發現@ControllerAdvice其實對異常處理並沒有什麼幫助,但是如果@ExceptionHandler 不配合 @ControllerAdvice使用,有一種處理方案,寫一個BaseController然後 寫一個有@ExceptionHandler的方法,然後所有需要的Controller都繼承BaseController,很明顯這樣很麻煩而且程式碼入侵性太強。差評!
上程式碼:
package com.wugz.app.controller.exception; import org.springframework.ui.Model; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.ModelAndView; import com.wugz.app.utils.BusinessException; /* 通過@ControllerAdvice,可以對於控制器的全域性配置放在同一位置, 註解了@Controller的類的方法可使用 @ExceptionHandler @InitBinder @MoudelAttribute 註解到方法上 以上這些對所有註解了@requestMapping的方法有效 @ExceptionHandler 用於全域性處理控制器的異常 @InitBinder 用來設定WebDataBinder ,WebDataBinder 用來自動繫結前臺請求引數到Model @MoudelAttribute 作用是繫結鍵值對到Model中,此處是讓全域性的@RequestMapping都能獲取到此處設定的鍵值對 */ //宣告這是一個控制器 建言 @ControllerAdvice 結合了 @Component 所以可以自動註冊為bean ,如果這個類只有 @Component 則@ExceptionHandler 不生效 @ControllerAdvice public class ExceptionHandlerAdvice { /*** * * @Description(功能描述) : 集中處理@requestMapping中丟擲的 Exception 異常 * @author(作者) : 吳桂鎮 * @date (開發日期) : 2017年11月30日 下午3:01:06 * @exception : * @param ex * @param request * @return Object */ @ExceptionHandler(value=Exception.class) //不設定value 則攔截所有的 Exception @ResponseBody public Object exception(Exception ex,WebRequest request) { System.out.println("exception"); ModelAndView mv = new ModelAndView(); mv.addObject(123); return "return Exception"; } /*** * * @Description(功能描述) : 處理@requestMapping中丟擲的 業務異常 * @author(作者) : 吳桂鎮 * @date (開發日期) : 2017年11月30日 下午3:01:31 * @exception : * @param ex * @param request * @return Object */ @ExceptionHandler(value=BusinessException.class) @ResponseBody public Object businessException(Exception ex,WebRequest request) { System.out.println("businessException"); return "return BusinessException"; } @ModelAttribute public void addAttribute(Model model) { System.out.println("addAttribute"); } @InitBinder public void initBinder(WebDataBinder binder) { System.out.println("initBinder"); } }
方式二 實現HandlerExceptionResolver
這個就不廢話直接上程式碼了
package com.wugz.app.controller.exception; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSONObject; import com.wugz.app.utils.BusinessException; /** * * @ClassName(類名) : MyExceptionHandler * @Description(描述) : 通過實現HandlerExceptionResolver 也可以進行統一的異常處理 * ,比較這種方式 增加了對目標方法相關資訊的獲取,可以根據這些資訊進行相關的記錄 * @author(作者) :吳桂鎮 * @date (開發日期) :2017年11月30日 下午3:23:27 * */ @Component public class MyExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); Annotation an = method.getAnnotation(ResponseBody.class); JSONObject result = new JSONObject(); //異常方法沒有@ResponseBody 註釋 直接返回頁面將錯誤資訊 if(an == null) { //處理異常 resolverException(ex, result); //模擬跳轉到處理頁面 mv.setViewName("index"); //將異常資訊放置到前臺 接受程式碼示例 ${requestScope.errorMessage.msg} 或者 //Object msg = request.getAttribute("errorMessage"); 然後在 js中 var result = '<%=msg%>'; 此時的result 是一個json格式的字串 mv.addObject("errorMessage", result); //異常方法有@ResponseBody 則返回json 字串 }else { //處理異常 resolverException(ex, result); //將返回資訊寫到 ResponseBody中 不跳轉頁面 就像 使用了 @ResponseBody response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control", "no-cache, must-revalidate"); try { response.getWriter().write(result.toJSONString()); } catch (IOException e) { e.printStackTrace(); } } return mv; } //處理異常 private void resolverException(Exception ex, JSONObject result) { if(ex instanceof BusinessException) { resolverBussinessException(ex, result); } else { resolverOtherException(ex, result); } } /* * 處理業務層異常 */ private void resolverBussinessException(Exception ex, JSONObject result) { //BusinessException businessException = (BusinessException) ex; result.put("msg", "業務異常"); } /* * 處理其他異常 */ private void resolverOtherException(Exception ex, JSONObject result) { result.put("msg", "系統異常"); } }
ps:一般來講這兩種方式沒啥不同,都比較簡單
第一種因為會寫到統一Controller配置的類中,相對來講配置的管理相對集中一點。
第二種因為實現了HandlerExceptionResolver 可以獲取到handler,這個Object 的 handler 可以強制轉換成HandlerMethod,這個HandlerMethod就是原始碼裡HandlerMapping傳給HandlerAdapter進行引數解析的重要因素,通過他可以獲取很多資訊,這一點優勢是第一種方法沒有的!
注:修改後的程式碼
@ExceptionHandler(value=Exception.class) //不設定value 則攔截所有的 Exception
@ResponseBody
public Object exception(Exception ex,WebRequest request,HandlerMethod handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
System.out.println("exception");
ModelAndView mv = new ModelAndView();
mv.addObject(123);
return "return Exception";
}
可以發現增加引數HandlerMethod spring也可以幫我們賦值,可以說這兩種的區別很小了。。。。
補充: 其實還是有些區別的,具體看程式碼註釋吧,所以說如果需要到HandlerMethod 獲取資訊,最好的方式還是實現HandlerExceptionResolver,另外在使用HandlerMethod 的時候一定判斷是否為空
/***
*
* @Description(功能描述) : 集中處理@requestMapping中丟擲的 Exception 異常 ,如果有些異常在 HandlerMethod 為空的時候就丟擲了
* 比如上傳檔案大小超出限制的 MaxUploadSizeExceededException HandlerMethod為空 則不會進入當前的異常捕獲方法
* 如果去掉引數HandlerMethod 則會進入 例如nohandlerException
* @author(作者) :
* @date (開發日期) : 2017年11月30日 下午3:01:06
* @exception :
* @param ex
* @param request
* @param HandlerMethod 可以獲取到異常方法的相關資訊,利用反射
* @return Object
*/
@ExceptionHandler(value=Exception.class) //不設定value 則攔截所有的 Exception
@ResponseBody
public Object exception(Exception ex,WebRequest request,HandlerMethod handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
System.out.println("exception");
ModelAndView mv = new ModelAndView();
mv.addObject(123);
return "return Exception";
}
@ExceptionHandler(value=Exception.class) //不設定value 則攔截所有的 Exception
@ResponseBody
public Object nohandlerException(Exception ex,WebRequest request) {
System.out.println("nohandlerException");
ModelAndView mv = new ModelAndView();
mv.addObject(123);
return "return nohandlerException";
}