7.【原創】Spring Mvc自定義DispatcherServlet類,處理404異常

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類都是我自定義的類,具體使用時,可依情況進行對應替換。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章