0
点赞
收藏
分享

微信扫一扫

无所遁形,全局异常捕获



文章目录


  • ​​一、前言​​
  • ​​二、Spring全局异常编写(3.4 全局异常第三种,实现HandlerExceptionResolver接口)​​

  • ​​2.1 定义异常处理类,实现HandlerExceptionResolver接口​​
  • ​​2.2 使用注解或xml配置的方式,将这个异常处理类注入到spring ioc容器​​
  • ​​2.3 实际业务逻辑中​​

  • ​​三、SpringMVC全局异常编写​​

  • ​​3.1 定义服务器错误WEB.XML整合Spring MVC​​

  • ​​3.1.1 web.xml​​
  • ​​3.1.2 applicationContext.xml​​

  • ​​3.2 Spring全局异常第一种,代码侵入方式/Controller增强方式( Advising Controllers)【@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常及其子异常 】​​

  • ​​3.2.1 异常抛出:业务逻辑中throw抛出​​
  • ​​3.2.2 第一种,异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常​​
  • ​​3.2.3 第二种,全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常​​

  • ​​3.3 Spring全局异常第二种,xml配置文件方式【配置SimpleMappingExceptionResolver到springioc容器中】​​

  • ​​3.3.1 异常抛出:业务逻辑中throw抛出​​
  • ​​3.3.2 异常捕获:applicationContext.xml,配置SimpleMappingExceptionResolver到springioc容器中​​
  • ​​3.3.3 对应500错误的view jsp页面​​

  • ​​3.4 Sping全局异常第三种,实现异常捕获接口HandlerExceptionResolver​​

  • ​​3.4.1 异常抛出:业务逻辑中throw抛出​​
  • ​​3.4.2 实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法​​
  • ​​3.4.3 异常捕获处理类注入到Spring IOC容器​​

  • ​​3.5 使用Restful的Controller可以使用@ResponseBody处理错误​​

  • ​​3.5.1 自定义类ErrorInfo​​
  • ​​3.5.2 一个@ResponseBody返回一个错误实例​​


  • ​​四、springboot如何添加全局异常捕获类(全局异常处理第一种,controller增强)​​

  • ​​4.1 业务逻辑代码中抛出异常​​
  • ​​4.2 定义全局异常处理类​​
  • ​​4.3 运行结果​​

  • ​​五、面试金手指(面试如何回答,面试语言组织)​​

  • ​​5.1 起手式,全局异常捕获的两种方式​​
  • ​​5.2 五种方式​​

  • ​​六、小结​​


一、前言

为什么要额外定义一个全局异常处理类?

回答:logger.error()等打印到处都有,开发的时候很不方便,所以将异常统一处理。

二、Spring全局异常编写(3.4 全局异常第三种,实现HandlerExceptionResolver接口)

2.1 定义异常处理类,实现HandlerExceptionResolver接口

定义异常处理类,实现HandlerExceptionResolver接口

package org.xxx.ac.zpk.exception;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.wpwl.ac.utils.JSONUtils;
import org.wpwl.ac.zpk.entity.ApiResult;
/**
* 异常处理
* @author xxx
*
*/
public class ExceptionHandler implements HandlerExceptionResolver {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
/* 使用response返回 */
response.setStatus(HttpStatus.OK.value()); // 设置状态码
response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 设置ContentType
response.setCharacterEncoding("UTF-8"); // 避免乱码
response.setHeader("Cache-Control", "no-cache, must-revalidate");
try {
ApiResult json = getExceptionResult(ex); // 调用自定义方法,得到异常处理结果
response.getWriter().write(JSONUtils.bean2Json(json));
} catch (IOException e) {
logger.error("与客户端通讯异常:" + e.getMessage(), e);
}

logger.debug("异常:" + ex.getMessage(), ex);
return mv;
}

private ApiResult getExceptionResult(Exception ex) {
ApiResult result;
// 根据不同的异常类型进行不同处理
if (ex instanceof WPException) {
result = ApiResult.err(ex.getMessage());
} else {
result = ApiResult.err("服务异常!");
}
return result;
}

}

2.2 使用注解或xml配置的方式,将这个异常处理类注入到spring ioc容器

<bean id="exceptionResolver" class="org.wpwl.ac.zpk.exception.ExceptionHandler"/>

2.3 实际业务逻辑中

package org.wpwl.ac.zpk.exception;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.wpwl.ac.zpk.entity.ApiResult;

public class ExceptionController {

// private static final Logger logger = LoggerFactory.getLogger(BaseController.class); // 注释掉异常打印

@ExceptionHandler
@ResponseBody
public ApiResult exception(HttpServletRequest request, Exception e) {
//添加自己的异常处理逻辑,如日志记录   
request.setAttribute("exceptionMessage", e.getMessage());
// e.printStackTrace(); // 标准代码中不使用这个打印方式
// logger.error(e.getMessage()); // 这样打印会导致到处都有,太杂乱了
// 根据不同的异常类型进行不同处理 就是上面的getExceptionResult()方法
if(e instanceof WPException){
return ApiResult.err(e.getMessage());
}
else{
return ApiResult.err("服务异常!");
}
}
}

三、SpringMVC全局异常编写

Spring MVC开发WEB应用时捕获全局异常的方法基本有两种:


  1. WEB.XML,就是指定error-code和page到指定地址,这也是最传统和常见的做法
  2. 用Spring的全局异常捕获功能,这种相对可操作性更强一些,可根据自己的需要做一后善后处理,比如日志记录等。

SO,列出Spring-MVC做WEB开发时常用全局异常捕获的几种解决方案抛砖引玉,互相没有依赖,每个都可单独使用

3.1 定义服务器错误WEB.XML整合Spring MVC

3.1.1 web.xml

<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>

<!-- 未捕获的错误,同样可指定其它异常类,或自定义异常类 -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/uncaughtException</location>
</error-page>

3.1.2 applicationContext.xml

<!-- 错误路径和错误页面,注意指定viewResolver -->
<mvc:view-controller path="/404" view-name="404"/>
<mvc:view-controller path="/500" view-name="500"/>
<mvc:view-controller path="/uncaughtException" view-name="uncaughtException"/>

小结:第一种方式,定义服务器错误WEB.XML整合Spring MVC,好了。

3.2 Spring全局异常第一种,代码侵入方式/Controller增强方式( Advising Controllers)【@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常及其子异常 】

3.2.1 异常抛出:业务逻辑中throw抛出

@Controller
public class MainController {
@ResponseBody
@RequestMapping("/")
public String main(){
throw new NullPointerException("NullPointerException Test!");
}
}

3.2.2 第一种,异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常

//注意使用注解@ControllerAdvice作用域是全局Controller范围,即必须与抛出异常的method在同一个controller
//可应用到所有@RequestMapping类或方法上的@ExceptionHandler、@InitBinder、@ModelAttribute,在这里是@ExceptionHandler
@ControllerAdvice
public class AControllerAdvice {
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIOException(NullPointerException ex) {
return ClassUtils.getShortName(ex.getClass()) + ex.getMessage();
}
}

3.2.3 第二种,全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常

为了确保@ResponseStatus标注的异常(就是3.2.2 )被Spring框架处理,可以这样编写全局异常处理类:

@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";

@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;

ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW); // 就是跳转到名为error.jsp的页面
return mav;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>500 Error</title>
</head>
<body>
<% Exception ex = (Exception)request.getAttribute("exception"); %> // 得到ex
<H2>Exception: <%= ex.getMessage()%></H2> // 使用ex
<P/> // 空格
<% ex.printStackTrace(new java.io.PrintWriter(out)); %> // 使用ex
</body>
</html>

第二种方式,Spring全局异常,代码侵入方式/Controller增强方式( Advising Controllers),完成。

3.3 Spring全局异常第二种,xml配置文件方式【配置SimpleMappingExceptionResolver到springioc容器中】

3.3.1 异常抛出:业务逻辑中throw抛出

@Controller
public class MainController {
@ResponseBody
@RequestMapping("/")
public String main(){
throw new NullPointerException("NullPointerException Test!");
}
}

3.3.2 异常捕获:applicationContext.xml,配置SimpleMappingExceptionResolver到springioc容器中

applicationContext.xml配置文件:

<!-- 全局异常配置 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">errors/500</prop>
<prop key="java.lang.Throwable">errors/500</prop>
</props>
</property>

<property name="statusCodes">
<props>
<prop key="errors/500">500</prop>
</props>
</property>

<!-- 设置日志输出级别,不定义则默认不输出警告等错误日志信息 -->
<property name="warnLogCategory" value="WARN"></property>

<!-- 默认错误页面,当找不到上面mappings中指定的异常对应视图时,使用本默认配置 -->
<property name="defaultErrorView" value="errors/500"></property>

<!-- 默认HTTP状态码 -->
<property name="defaultStatusCode" value="500"></property>
</bean>

3.3.3 对应500错误的view jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>500 Error</title>
</head>
<body>
<% Exception ex = (Exception)request.getAttribute("exception"); %> // 得到ex
<H2>Exception: <%= ex.getMessage()%></H2> // 使用ex
<P/> // 空格
<% ex.printStackTrace(new java.io.PrintWriter(out)); %> // 使用ex
</body>
</html>

3.4 Sping全局异常第三种,实现异常捕获接口HandlerExceptionResolver

3.4.1 异常抛出:业务逻辑中throw抛出


ps:这里全局异常处理和 系统异常/自定义异常 无关


异常抛出:抛出自定义异常

@ResponseBody
@RequestMapping("/ce")
public String ce(CustomException e){
throw new CustomException("msg",e); // 可以throw new 系统异常();
}

这里抛出自定义异常

public class CustomException extends RuntimeException {

public CustomException(){
super();
}

public CustomException(String msg, Throwable cause){
super(msg, cause);
//Do something...
}
}

3.4.2 实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法

public class CustomHandlerExceptionResolver implements HandlerExceptionResolver{

@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("e", e);
//这里可根据不同异常引起类做不同处理方式,本例做不同返回页面。
String viewName = ClassUtils.getShortName(e.getClass()); // viewName
return new ModelAndView(viewName, model);
}
}

新的的HandlerExceptionResolver实现类只需在配置文件中定义即可,可以配置优先级。DispatcherServlet初始化HandlerExceptionResolver的时候会自动寻找容器中实现了HandlerExceptionResolver接口的类,然后添加进来。

3.4.3 异常捕获处理类注入到Spring IOC容器

<bean class="cn.bg.controller.CustomHandlerExceptionResolver"/>

3.5 使用Restful的Controller可以使用@ResponseBody处理错误

使用Restful的Controller可以使用@ResponseBody处理错误,首先定义一个错误:

3.5.1 自定义类ErrorInfo

public class ErrorInfo {
public final String url;
public final String ex;

public ErrorInfo(String url, Exception ex) {
this.url = url;
this.ex = ex.getLocalizedMessage();
}
}

3.5.2 一个@ResponseBody返回一个错误实例

通过一个@ResponseBody返回一个错误实例:

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MyBadDataException.class)
@ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
return new ErrorInfo(req.getRequestURL(), ex);
}

使用Restful的Controller可以使用@ResponseBody处理错误,好了。

四、springboot如何添加全局异常捕获类(全局异常处理第一种,controller增强)

作为后端开发专注开发接口。对于程序中出现的异常如果不进行处理,将报错信息直接返回到前端是不优雅的,因此需要对异常进行捕获和处理,但是每个接口都单独处理异常则显得代码十分臃肿,因此写了个异常捕获类对全局出现的异常进行统一处理。

4.1 业务逻辑代码中抛出异常

@RestController
public class HelloController {

@RequestMapping("/hello")
public String hello(String idNum) {
throw new BusinessException("抛出业务异常");
}
}

4.2 定义全局异常处理类

全局异常处理类要处理整个系统中的所有异常,当然包括刚才那个 HelloController 中的 BusinessException


金手指:全局异常处理包括两个维度 整个系统 和 各种类型异常


//@ControllerAdvice 注解@ControllerAdvice作用域是全局Controller范围,即必须与抛出异常的method在同一个controller
@ControllerAdvice
public class GlobalExceptionHandler {

//@ExceptionHandler 该注解声明异常处理方法
//value=BusinessException.classb表示处理抛出的BusinessException异常
@ExceptionHandler(value = BusinessException.class)
public ReturnResult defaultErrorHandler(HttpServletRequest req, Exception e) {
ReturnResult result = new ReturnResult(); //出现异常后返回到前端的信息
result.setCode("-1");
result.setMsg("出现了业务异常");
//打印异常信息
logger.error("调用失败", e);

return result;
}
}

1、使用 @ControllerAdvice 注解后,GlobalExceptionHandler 类变为了全局controller异常处理类,即必须与抛出异常的method在同一个controller。

2、 @ExceptionHandler(value = BusinessException.class) 标注在defaultErrorHandler()这个方法上,其中,

@ExceptionHandler 表示这个方法是一个异常处理方法

value=BusinessException.class 表示这个异常处理方法,处理抛出的BusinessException异常

3、异常处理方法内的逻辑:传递给前端 + 服务端自身的打印

ReturnResult result = new ReturnResult(); 表示传递给前端

logger.error(“调用失败”, e); 表示服务端自身的打印

4.3 运行结果

访问/hello资源时被2中捕获BusinessException异常的类捕获,并返回前端数据{“code”:"-1", “message”:“出现业务异常”}, 日志打印信息如下:

无所遁形,全局异常捕获_异常处理

五、面试金手指(面试如何回答,面试语言组织)

5.1 起手式,全局异常捕获的两种方式

Spring MVC开发WEB应用时捕获全局异常的方法基本有两种:


  1. WEB.XML,就是指定error-code和page到指定地址,这也是最传统和常见的做法(一种方式)
  2. 用Spring的全局异常捕获功能,这种相对可操作性更强一些,可根据自己的需要做一后善后处理,比如日志记录等。(三种方式)

最后加上一种:使用@ResponseBody处理错误。

5.2 五种方式


第一个方式:提供web.xml和applicationContext.xml
web.xml文件中,指定error-code和页面地址location;
applicationContext.xml文件中,指定path和view-name
<error-page
<exception-type java.lang.Exception </exception-type
<location /uncaughtException </location
</error-page
<mvc:view-controller path="/uncaughtException" view-name=“uncaughtException”/>



第二种方式:Spring全局异常,代码侵入方式/Controller增强方式( Advising Controllers)
1、业务逻辑中throw抛出异常
2、异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常
3、当然,也可以全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常



第三种方式:Spring全局异常第二种,xml配置文件方式
1、业务逻辑中throw抛出异常
2、applicationContext.xml配置文件中,配置SimpleMappingExceptionResolver到springioc容器中,对于到跳转页面



第四种方式:Spring全局异常第三种,实现异常捕获接口HandlerExceptionResolver
1、业务逻辑中throw抛出异常
2、实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法,跳转到页面
3、xml配置文件:异常捕获处理类注入到Spring IOC容器



注意:第三种和第四种都是全局配置方式,一样的,只是第三种是在applicationContext.xml中使用HandlerExceptionResolver实现类,使用了系统自带的SimpleMappingExceptionResolver类,第四种自定义HandlerExceptionResolver接口实现类,重写resolveException()方法
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
所以,SimpleMappingExceptionResolver是HandlerExceptionResolver接口的实现类,good



第五种方式:使用Restful的Controller可以使用@ResponseBody处理错误
1、自定义类ErrorInfo
2、一个@ResponseBody返回一个错误实例ErrorInfo实例


六、小结

【Spring】Spring全局异常捕获如何编写(五种方式,三种全局异常处理方式),完成了。



举报

相关推荐

0 条评论