Spring boot/Spring 统一错误处理方案的使用

当咱们开发spring web应用程序时,对于如IOException,ClassNotFoundException之类的检查异常,每每编译器会提示程序员采用try-catch进行显式捕获,而对于像ClassCastException,NullPointerException这类非检查异常,编译器是不会提示你了,这每每也是能体现程序员代码编写能力的一个方面。html

在spring web特别是spring-boot应用中,当一个请求调用成功时,通常状况下会返回json格式的对象,就像下面图所示:java

但若是请求抛出了一个RuntimeException呢?若是咱们不作处理,再次调用时将出现下面的页面:git

也就是说当调用出现错误时,spring-boot默认会将请求映射到/error路径中去,若是没有相应的路径请求处理器,那么就会返回上面的Whitelabel错误页面。程序员

一、自定义错误处理页面

固然对运行时异常不作处理是不可能的啦!一般的作法是自定义统一错误页面,而后返回。按照上面的思路,咱们实现一个请求路径为/error的控制器,控制器返回一个资源路径地址,定义请求映射路径为/error的控制器并实现ErrorController接口,代码以下:github

MyErrorPageControllerweb

package com.example.demo.controller.handler.errorpage;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 
 * The class MyErrorPageController.
 *
 * Description:自定义错误页面
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@Controller
public class MyErrorPageController implements ErrorController {
    
    @RequestMapping("/error")
    public String handleError() {
    	return "error.html"; // 该资源位于resources/static目录下
    }
    
    @Override
    public String getErrorPath() {
    	return null;
    }
}
复制代码

而后在reosurces/static目录下创建error.html文件:spring

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>这是个错误页面!存放在resources/static目录下,spring-boot发生错误时默认调用</h1>
</body>
</html>
复制代码

再次请求http://localhost:7000/demo/getUserInfoWithNoHandler.json,以下:json

二、使用@ControllerAdvice@ResponseBody@ExceptionHandler统一处理异常

在spring中可使用上面3个注解进行统一异常处理,默认状况下咱们能够针对系统中出现的某种类型的异常定义一个统一的处理器handler,好比说系统抛出了一个NullPointerException,那么咱们能够定义一个专门针对NullPointerException的处理器,代码以下:浏览器

getUserInfoWithNullPointerException接口bash

/**
 * 测试空指针错误的处理
 * @return
 * @throws NullPointerException
 */
@RequestMapping(value = "getUserInfoWithNullPointerException.json", method = RequestMethod.GET)
public Student getUserInfoWithNullPointerException() throws NullPointerException {
	throw new NullPointerException();
}
复制代码

NullPointerExceptionHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
 * 
 * The class NullPointerExceptionHandler.
 *
 * Description:处理空指针
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@ControllerAdvice
public class NullPointerExceptionHandler {
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ErrorReturn dealNullPointerException() {
        e.printStackTrace();
    	ErrorReturn error = new ErrorReturn();
    	error.setReturnCode("-1");
    	error.setDesc("出现空指针异常啦!");
    	return error;
    }
}
复制代码

浏览器执行:http://localhost:7000/demo/getUserInfoWithNullPointerException.json

一样的道理,若是咱们还须要为其余的运行时异常提供统一的处理器,那么也能够像上面同样为每个异常类型定义一个处理器,好比咱们又想为ArithmeticException定义处理器,那么咱们只须要创建一个类或者方法,而后在方法上的@ExceptionHanler注解内加上ArithmeticException.class指定异常类型便可。

不过你有没有发现,这样为每种异常类型定义一个异常处理类或者方法,由于运行时异常类型特别多,不可能为每种类型都指定一个处理器类或方法,针对这种状况,spring也是能够解决的。若是咱们没有为某种特定类型异常,如ArithmeticException定义处理器,那么咱们能够定义一个Exception或者Throwable处理器统一处理。

这样作的好处是,减小了处理器类的数量,同时将异常处理转移到父类上面去,这也是继承的一大优点吧!可是,当你既定义了特定类型的异常,同时又定义了Exception异常的处理器,那么要当心了,这里不必定有优先级的关系,也就是说不必定会出现只执行父异常处理器的状况,多是只执行A处理器,而不执行B处理器或者只执行B处理器,不执行A处理器。如NullPointerExceptionHandler异常会向Exception异常传递(但ArithmeticException不会向Exception传递)

如今假设咱们既定义上面的NullPointerExceptionHandler,又定义了下面的ExceptionThrowableHandler,那么当发生NullPointerException时,就会默认执行ExceptionThrowableHandler的方法。

ExceptionThrowableHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
 * 
 * The class ExceptionThrowableHandler.
 *
 * Description:有些异常会向高级别异常传递(但ArithmeticException不会向Exception传送)
 *
 * @author: huangjiawei
 * @since: 2018年6月13日
 * @version: $Revision$ $Date$ $LastChangedBy$
 *
 */
@ControllerAdvice
public class ExceptionThrowableHandler {
    
    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public ErrorReturn dealThrowable() {
    	ErrorReturn error = new ErrorReturn();
    	error.setDesc("处理Throwable!");
    	error.setReturnCode("-1");
    	return error;
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ErrorReturn dealCommonException() {
    	ErrorReturn error = new ErrorReturn();
    	error.setReturnCode("-1");
    	error.setDesc("公共异常处理!");
    	return error;
    }
}
复制代码

浏览器执行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json

能够发现只执行Exception的处理器,没有执行空指针的处理器,也就是异常处理往上传送了。下面再来看看抛出ArithmeticException的状况:

getUserInfoWithArithmeticException.json

/**
 * 测试ArithmeticException错误的处理
 * @return
 * @throws ArithmeticException
 */
@RequestMapping(value = "getUserInfoWithArithmeticException.json", method = RequestMethod.GET)
public Student getUserInfoWithArithmeticException() throws ArithmeticException {
	throw new ArithmeticException();
}
复制代码

ArithmeticExceptionHandler.java

package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

@ControllerAdvice
public class ArithmeticExceptionHandler {
    /**
     * 处理ArithmeticException异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ArithmeticException.class)
    public ErrorReturn dealArithmeticException() {
    	ErrorReturn errorObject = new ErrorReturn();
    	errorObject.setReturnCode("-1");
    	errorObject.setDesc("算数处理出现异常!");
    	return errorObject;
    }
}
复制代码

浏览器执行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json

结果发现异常处理并无往上层的ExceptionHandler传送。

总结:对于既定义特定类型的处理器,又定义Exception等父类型的处理器时要特别当心,并非全部的异常都会往上级处理,若是咱们想只减小处理器类的数量,不想为每种特定类型的处理器添加类或者方法,那么小编建议使用instanceof关键字对异常类型进行判断便可。

以下面的代码,咱们只创建一个公共的异常处理器,处理Exception异常,同时使用instanceof进行判断。

@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException(Exception e) {
    ErrorReturn error = new ErrorReturn();
    // 此处能够采用 instanceof 判断异常类型
    if (e instanceof ArithmeticException) {
    	error.setReturnCode("-1");
    	error.setDesc("算数异常处理!");
    	return error;
    }
    System.err.println("exception");
    error.setReturnCode("-1");
    error.setDesc("公共异常处理!");
    return error;
}
复制代码

浏览器执行抛出ArithmeticException的接口,以下:

本文代码地址: github.com/SmallerCode…

谢谢阅读,欢迎start,指正!

相关文章
相关标签/搜索