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类都是我自定义的类,具体使用时,可依情况进行对应替换。

原文地址:https://www.cnblogs.com/Nick-Hu/p/13650453.html