1.原因:
自己在寫純接口服務的時候,使用Spring Mvc方式在web.xml中配置攔截器,使用@RestControllerAdvice注解定義了一個全局的異常處理器,在SpringBoot中,它可正常攔截到類似NoHandlerFoundException(404)、500、406等異常,但是在Spring Mvc確無法做到。在使用Spring Mvc時,此框架通過DispatcherServlet類進行轉發請求時,例如404此種異常會直接在DispatcherServlet類中拋出,然后由Tomcat捕獲進行錯誤頁面的響應處理,並不會轉發到Controller層中。也就無法到達@RestControllerAdvice注解的全局異常處理類中處理。
2.處理:
2.1.繼承DispatcherServlet,自定義轉發器,重寫noHandlerFound方法處理404異常,因為此方法返回值是void,所以采用拋出自定義的異常,讓@RestControllerAdvice的全局異常處理類處理。再重寫processHandlerException方法處理其他異常,例如500,406等。
2.2.在web.xml中將原先的DispatcherServlet替換成自定義的CustomizeDispatcherServlet類即可。話不多說,上代碼。
3.實現:
3.1.自定義轉發器DispatcherServlet重寫noHandlerFound和processHandlerException方法
package cn.drip.dripblog.filter; import cn.drip.dripblog.constant.consist.DripException; import cn.drip.dripblog.constant.enums.ExpStatus; import cn.drip.dripblog.model.dto.Result; import com.alibaba.fastjson.support.spring.FastJsonJsonView; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * @Author: Chunhai.Hu * @Date: 2020/9/8 0008 15:46 * @Last Modified by: Chunhai.Hu * @Last Modified time: 2020/9/8 0008 15:46 * @Desc: 自定義DispatcherServlet,重寫noHandlerFound方法,處理HTTP通用異常,如404 */ public class CustomizeDispatcherServlet extends DispatcherServlet { // 拋出404的異常:交由GlobalExceptionHandler處理 @Override protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { throw new NoHandlerFoundException(request.getMethod(), request.getRequestURL().toString(), new HttpHeaders()); } // 攔截對應異常:攔截除404之外的HTTP異常 @Override protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 共用參數 ModelAndView model = new ModelAndView(); FastJsonJsonView view = new FastJsonJsonView(); Map<String, String> map = new HashMap<String, String>(); if (ex instanceof HttpRequestMethodNotSupportedException) { // 處理405異常:請求方式不支持 map.put("status", String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value())); map.put("message", ex.getMessage()); } else {
// 不想自定義處理的異常:交還父類處理 return super.processHandlerException(request, response, handler, ex); } // 設置返回 view.setAttributesMap(map); model.setView(view); return model; } }
3.2.使用RestControllerAdvice注解定義全局的異常處理器GlobalExceptionHandler
package cn.drip.dripblog.filter; import cn.drip.dripblog.constant.consist.DripException; import cn.drip.dripblog.constant.enums.ExpStatus; import cn.drip.dripblog.model.dto.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; /* * @Created with IntelliJ IDEA. * @Author: Chunhai.Hu * @Date: 2020/6/29 2:38 * @1.Desc: 全局異常處理,如果使用@RestControllerAdvice 注解則會將返回的數據類型轉換成JSON格式 * @2.在Spring mvc中404,500,505等異常通過DispatcherServlet類拋出被tomcat捕獲, * @無法到達Controller,也就無法到達@ControllerAdvice * @處理方式:聲明自定義的CustomizeDispatcherServlet類,重寫noHandlerFound和processHandlerException方法處理 */ @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * @param req * @param e * @return * @處理: 自定義異常 */ @ExceptionHandler(DripException.class) public Result DripExceptionHandler(HttpServletRequest req, DripException e) { e.printStackTrace(); logger.error("DripException Exception Cause by:{}", e.getMessage()); return new Result(e.getStatus(), e.getMessage(), e.getExpMsgAttach()); } /** * @處理404異常 * @param req * @param e * @return */ @ExceptionHandler(NoHandlerFoundException.class) public Result handlerNoFoundException(HttpServletRequest req, NoHandlerFoundException e) { logger.error("NoHandlerFoundException Exception Cause by:{}", e.getMessage()); return new Result(String.valueOf(HttpStatus.NOT_FOUND.value()), e.getMessage()); } /** * @param req * @param e * @return * @處理:空指針異常 */ @ExceptionHandler(NullPointerException.class) public Result nullPointerExceptionHandler(HttpServletRequest req, NullPointerException e) { e.printStackTrace(); logger.error("NullPointerException Exception Cause by:{}", e.getMessage()); return new Result(ExpStatus.SERVICE_BUSY); } /** * @param req * @param e * @return * @處理:未涉及的異常 */ @ExceptionHandler(Exception.class) public Result ExceptionHandler(HttpServletRequest req, Exception e) { e.printStackTrace(); logger.error("Exception Cause by:{}", e.getMessage()); return new Result(ExpStatus.SERVICE_BUSY); } }
3.3.將web.xml中將原先的DispatcherServlet替換成自定義的CustomizeDispatcherServlet類
<?xml version="1.0" encoding="UTF-8"?> <web-app 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_3_0.xsd" version="3.0"> <!--1.1:配置Spring監聽器,加載spring核心容器配置:默認只加載WEB-INF下的applicationContext.xml文件--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--1.2:設置Spring配置文件路徑--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--步驟2.1:配置spring的前端控制器,讓所有請求都由其處理(此為自定義的DispatcherServlet)--> <servlet> <servlet-name>CustomizeDispatcherServlet</servlet-name> <servlet-class>cn.drip.dripblog.filter.CustomizeDispatcherServlet</servlet-class> <!--配置初始化參數:讀取springmvc的配置文件:使用類路徑方式--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> <!--配置加載策略:一啟動時就加載--> <load-on-startup>1</load-on-startup> </servlet> <!--2.2:配置映射路徑及攔截范圍--> <servlet-mapping> <servlet-name>CustomizeDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
4.注意事項:
4.1.以上代碼中的DripException、ExpStatus、Result和Status類都是我自定義的類,具體使用時,可依情況進行對應替換。