自定义ControllerExceptionHandler
package com.wh.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
/**
* @author wanghan
* @description 处理异常的拦截类
* @date 2021/1/22
**/
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* @description //错误异常处理
* @param request : 出错的 URL
* e : 异常
* @return ModelAndView
* @date 2021/1/22
* ExceptionHandler //标识异常的注解
*/
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(HttpServletRequest request, Exception e) throws Exception {
logger.error("Request URL : {}, Exception : {}", request.getRequestURI(), e);
ModelAndView mv = new ModelAndView();
//放行加了状态码的异常
if (e instanceof HttpRequestMethodNotSupportedException) {
mv.addObject("url", request.getRequestURI());
mv.addObject("exception", e);
mv.setViewName("error/405");
return mv;
}
if (e instanceof MissingServletRequestParameterException) {
mv.addObject("url", request.getRequestURI());
mv.addObject("exception", e);
mv.setViewName("error/400");
return mv;
}
if (e instanceof NoHandlerFoundException) {
mv.addObject("url", request.getRequestURI());
mv.addObject("exception", e);
mv.setViewName("error/404");
return mv;
}
mv.addObject("url", request.getRequestURI());
mv.addObject("exception", e);
mv.setViewName("error/500");
return mv;
}
}
springmvc.xml增加配置
<context:component-scan base-package="com.wh" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven />
说明,加入注解的扫描,避免自定义的ControllerExceptionHandler类未装配,从而不起作用
web.xml的配置
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置DispatcherServlet的初始化參數:设置文件的路径和文件名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--默认的找不到是抛出的,这里修改下-->
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<!--加载的时候就创建-->
<load-on-startup>1</load-on-startup>
</servlet>
在DispatcherServlet源码中,找不到处理器时候的异常是不抛出的,因此需要设置为抛出,否则404的异常,无法被我们自定义的类给捕捉
/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
private boolean throwExceptionIfNoHandlerFound = false;
原因分析
在系统中出现异常后都会去交由ExceptionResolver异常处理器处理
@RequestMapping方法抛出异常后,Spring框架 try-catch的方法捕获异常, 正常逻辑发不发生异常都会走processDispatchResult流程 ,区别在于异常的参数是否为null 。
如果@RequestMapping方法抛出异常,拦截器的postHandle方法不执行,进入 processDispatchResult,判断入参 dispatchException,不为null , 代表发生异常,调用processHandlerException处理,
@Controller和@ControllerAdvice类可以具有 @ExceptionHandler处理控制器方法异常的方法
@ControllerAdvice
- @ControllerAdvice是一个@Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
- Spring4之前,@ControllerAdvice在同一调度的Servlet中协助所有控制器。Spring4已经改变:@ControllerAdvice支持配置控制器的子集,而默认的行为仍然可以利用。
- 在Spring4中, @ControllerAdvice通过annotations(), basePackageClasses(), basePackages()方法定制用于选择控制器子集。
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
此方法优先级最高
@ControllerAdvice
public class GlobalExceptionHandler {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
优先级次之
@ExceptionHandler
方法支持以下参数:
方法参数 | 描述 |
---|---|
异常类型 | 用于访问引发的异常。 |
| 用于访问引发异常的控制器方法。 |
| 对请求参数以及请求和会话属性的一般访问,而无需直接使用Servlet API。 |
| 选择任何特定的请求或响应类型(例如 |
| 强制会话的存在。结果,这种论据永远不会 |
| 当前经过身份验证的用户-可能是特定的 |
| 请求的HTTP方法。 |
| 当前请求的语言环境,取决于最具体的 |
| 与当前请求关联的时区,由决定 |
| 用于访问原始响应主体,如Servlet API所公开。 |
| 用于访问模型以进行错误响应。永远是空的。 |
| 指定在重定向的情况下要使用的属性(将附加到查询字符串中)和flash属性,这些属性将临时存储直到重定向后的请求。 |
| 与访问由于类级 |
| 用于访问请求属性。 |