SpringBoot 异常处理
一、自定义错误页面
SpringBoot默认的已经提供了一套处理异常的机制,一旦程序中出现异常,SpringBoot会向/error
的url发送请求,在SpringBoot中提供了一个叫BasicErrorController
来处理/error
请求(注意:以前的版本是BasicExceptionController
),在这个类中,就可以很明显的看到,传递的文件位置,以及传递了一个叫error的参数
BasicErrorController
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
public String getErrorPath() {
return null;
}
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
因此,我们想定义一个简单的错误页面,就很轻松了,在resources/templates
目录下建立一个叫error.html
的文件就可以了,如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span th:text="${error}"></span>
</body>
</html>
二、@ExceptionHandler
我们完全可以在出现异常的Controller中自定义注入
@GetMapping("/test")
public String testException(){
String str = null;
str.equals("aaa");
return "register";
}
@ExceptionHandler(value = {java.lang.NullPointerException.class})
public ModelAndView handleNullPointerException(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("error");
System.out.println("e = " + e);
return mv;
}
三、全局异常处理
上面的做法需要在每个Controller类中加入异常注入,肯定是不可取的,对于异常的处理,当然是最好全局都能用最好,SpringBoot为我们提供了@ControllerAdvice
来解决这个问题,其实就是将之前写在单独Controller里面的ExceptionHandler集中移入到一个类中进行处理
@ControllerAdvice
public class GlobalException {
// 空指针异常
@ExceptionHandler(value = {java.lang.NullPointerException.class})
public ModelAndView handleNullPointerException(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("error");
System.out.println("e = " + e);
return mv;
}
// 算术异常
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("error");
System.out.println("e = " + e);
return mv;
}
}
四、全局异常类的统一处理
很明显上面的类,如果要处理很多种异常,就需要建很多方法,这样实在太费时费力,可以使用SimpleMappingExceptionResovler
来进行统一处理
@Configuration
public class GlobalException {
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ArithmeticException","error");
properties.setProperty("java.lang.NullPointerException","error");
resolver.setExceptionMappings(properties);
//设置默认异常页面
resolver.setDefaultErrorView("error");
//设置默认异常对象
resolver.setExceptionAttribute("error");
return resolver;
}
}
五、自定义HandlerExceptionResolver
类处理异常
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
//可以设置不同的异常跳转不同的页面,当我这里处理的都是一个页面直接设置一个异常页面就可以了
mv.setViewName("error");
// if (e instanceof ArithmeticException){
// mv.setViewName("error");
// }
// else if(e instanceof ArithmeticException){
// mv.setViewName("error");
// }
mv.addObject("error",e);
return mv;
}
}
六、自定义异常处理
为了更加适用前后端分离的应用设置,将异常处理和之前的返回前端ResultVO
的信息再进行了一下封装
统一信息接口
public interface BaseInfoInterface {
Integer status();
String message();
}
返回信息枚举
public enum ResultCode implements BaseInfoInterface {
SUCCESS(1,"成功"),
// 常见错误
BODY_NOT_MATCH(400,"请求的数据格式不符!"),
SIGNATURE_NOT_MATCH(401,"请求的数字签名不匹配!"),
NOT_FOUND(404, "未找到该资源!"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
SERVER_BUSY(503,"服务器正忙,请稍后再试!"),
// 参数错误1001-1999
PARAM_IS_INVALID(1001,"参数无效"),
PARAM_IS_BLANK(1002,"参数为空"),
PARAM_TYPE_BAND_ERROR(1003,"参数类型错误"),
PARAM_NOT_COMPLETE(1004,"参数缺失"),
// 用户错误2001-2999
USER_NOT_LOGGED_IN(2001,"用户未登录"),
USER_LOGIN_ERROR(2002,"账户不存在或密码错误"),
USER_ACCOUNT_FORBIDDEN(2003,"账户已被禁用"),
USER_NOT_EXIST(2004,"用户不存在"),
USER_HAS_EXISTED(2005,"用户已存在"),
USER_PASS_ERROR(2006,"密码错误"),
USER_AUTHENTICATION_ERROR(2007,"认证失败"),
USER_AUTHORIZATION_ERROR(2008,"没有权限"),
// 服务器错误3001-3999
SERVER_OPTIMISTIC_LOCK_ERROR(3001,"操作冲突"),
SERVER_INNER_ERROR(3002,"服务器内部错误"),
SERVER_UNKNOW_ERROR(3003,"服务器未知错误"),
SERVER_EMPTY_RESULT_DATA_ACCESS_ERROR(3004,"没有找到对应的数据");
private Integer status;
private String message;
ResultCode(Integer status, String message) {
this.status = status;
this.message = message;
}
public Integer status(){
return this.status;
}
public String message(){
return this.message;
}
}
返回对象
@Data
public class ResultVO implements Serializable {
private Integer status;
private String message;
private Object data;
public ResultVO(ResultCode resultCode, Object data){
this.status = resultCode.status();
this.message = resultCode.message();
this.data = data;
}
public ResultVO(ResultCode resultCode){
this.status = resultCode.status();
this.message = resultCode.message();
}
public ResultVO(Integer status, String message){
this.status = status;
this.message = message;
}
// 返回成功
public static ResultVO success(){
ResultVO resultVO = new ResultVO(ResultCode.SUCCESS);
return resultVO;
}
// 返回成功
public static ResultVO success(Object data){
ResultVO resultVO = new ResultVO(ResultCode.SUCCESS,data);
return resultVO;
}
// 返回失败
public static ResultVO fail(ResultCode resultCode){
ResultVO resultVO = new ResultVO(resultCode);
return resultVO;
}
// 返回失败
public static ResultVO fail(ResultCode resultCode, Object data){
ResultVO resultVO = new ResultVO(resultCode,data);
return resultVO;
}
// 返回失败
public static ResultVO fail(Integer status, String message){
ResultVO resultVO = new ResultVO(status,message);
return resultVO;
}
}
自定义异常
@EqualsAndHashCode
@Data
public class BizRuntimeException extends RuntimeException {
private Integer status;
private String message;
public BizRuntimeException() {
}
public BizRuntimeException(String message){
super(message);
this.message = message;
}
public BizRuntimeException(Integer status, String message){
super(status+"");
this.status = status;
this.message = message;
}
public BizRuntimeException(BaseInfoInterface baseInfoInterface) {
super(baseInfoInterface.status() + "");
this.status = baseInfoInterface.status();
this.message = baseInfoInterface.message();
}
public BizRuntimeException(BaseInfoInterface baseInfoInterface, Throwable cause) {
super(baseInfoInterface.status() + "", cause);
this.status = baseInfoInterface.status();
this.message = baseInfoInterface.message();
}
}
全局异常配置
//@RestControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 自定义异常
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = BizRuntimeException.class)
@ResponseBody
public ResultVO bizExceptionHandler(HttpServletRequest request, BizRuntimeException e){
logger.error("发生业务异常,原因是:" + e.getMessage());
return ResultVO.fail(e.getStatus(),e.getMessage());
}
/**
* 处理空指针的异常
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public ResultVO exceptionHandler(HttpServletRequest request, NullPointerException e){
logger.error("发生空指针异常!原因是:",e);
return ResultVO.fail(ResultCode.BODY_NOT_MATCH);
}
/**
* 处理其他异常
* @param request
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultVO exceptionHandler(HttpServletRequest request, Exception e){
logger.error("未知异常!原因是:",e);
return ResultVO.fail(ResultCode.INTERNAL_SERVER_ERROR);
}
}
具体使用
@RestController
public class UserRestController {
@PostMapping("/userRest")
public boolean insert(User user) {
System.out.println("开始新增...");
//如果姓名为空就手动抛出一个自定义的异常!
if(user.getUsername() == null){
throw new BizRuntimeException(-1,"用户姓名不能为空!");
}
return true;
}
@PutMapping("/userRest")
public boolean update(User user) {
System.out.println("开始更新...");
//这里故意造成一个空指针的异常,并且不进行处理
String str=null;
str.equals("111");
return true;
}
@DeleteMapping("/userRest")
public boolean delete(User user) {
System.out.println("开始删除...");
//这里故意造成一个异常,并且不进行处理
Integer.parseInt("abc123");
return true;
}
}
Comments