我们知道在做SpingBoot时,难免会出现很多异常,想要快速解决这些异常必须要知道异常出在何处,又是何种错误,SpringBoot内置的已经有了一些异常处理,例如:RuntimeException、ArithmeticException、NullPointerException等等异常,但是仅仅这些异常是远远不够我们对于异常的断定和处理,所以才会延伸出局部异常处理、全局异常和深度异常处理等等,中心思想还是为了能够更加方便的了解出现异常时,知道是何种错误并快速处理,这样才不会盲目的去改bug。
SpringBoot默认的处理异常的机制,一旦程序中出现了异常会像/error 的 url 发送请求。默认BasicExceptionController 来处理/error 请求,然后跳转到默认显示异常的页面,也可以自定义error.html 页面。
先看一段局部异常处理:
package com.kdcrm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
public class ToIndexController {
@RequestMapping("/testException")
public void testException(){
//制造运行时异常
System.out.println(1/0);
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map exceptionHandler(Exception ex)
{
Map map=new HashMap<>();
map.put("code",101);
map.put("msg",ex.toString());
return map;
}
}
上述类中定义的异常处理,只能处理当前类中的异常,其他类中的捕获不到,所以我们引入了基于 @ControllerAdvice注解 的Controller层的全局异常处理:
package com.kdcrm.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandlerController {
/**
* 捕获Controller层的所有异常信息
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map exceptionHandler(Exception ex)
{
Map map=new HashMap<>();
map.put("code",203);
map.put("msg",ex.toString());
return map;
}
}
也可以将异常信息跳转到指定页面内,便于管理:
error.html:
<html lang="en" mlns:th="http://www.thymeleaf.org" xmlns:mlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>error页面title>
head>
<body>
<h1 th:text="${msg}">h1>
body>
html>
Controller层全局异常处理GlobalExceptionHandlerController:
package com.kdcrm.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandlerController {
/**
* 将异常信息跳转值指定error页面
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
public ModelAndView exceptionHandler2(Exception ex){
ModelAndView mav = new ModelAndView();
mav.addObject("msg",ex.toString());
mav.setViewName("error");
return mav;
}
}
上述是通过@ControllerAdvice注解的方式实现的全局异常处理,还有一种是实现HandlerExceptionResolver处理异常解析器接口的方式,两者原理是一样的,根据出现的是何种异常跳转至相应的页面,更加精准些:
package com.kdcrm.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class GlobalExceptionHandlerController implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
ModelAndView mav = new ModelAndView();
//判断不同异常类型,做不同的视图跳转
if(ex instanceof ArithmeticException) {
mav.setViewName("ArithmeticError");
}else if (ex instanceof NullPointerException){
mav.setViewName("NullpointerError");
}else if (ex instanceof IllegalAccessException){
mav.setViewName("IllegalAccessError");
}
mav.addObject("msg",ex.toString());
return mav;
}
}
我们可能会经常需要对传入的参数进行校验,如果数据比较少的时候还比较容易处理,但当数据比较多的时候会显得比较麻烦,而且处理不当的时候,还会代码重复。这时候就需要对参数进行校验了,而Spring Boot采用的是Validation对数据进行校验。
首先引入校验包依赖文件:
<dependency>
<groupId>org.hibernate.validatorgroupId>
<artifactId>hibernate-validatorartifactId>
<version>7.0.0.Finalversion>
dependency>
JSR 303 是Bean验证的规范 ,Hibernate Validator 是该规范的参考实现,它除了实现规范要求的注解外,还额外实现了一些注解。
包括一下注解:
注解 | 说明 |
---|---|
@AsserrtFalse | 元素必须为false |
@AssertTrue | 元素必须为true |
@DecimalMax(value) | 元素必须为数字,最大值为指定value |
@DecimalMin(value) | 元素必须为数字,最小值为指定value |
@Digits(integer,fraction) | 元素必须为数字,其值必须在可接受的范围内 |
@Null | 元素必须为null |
@NotNull | 元素必须不为null |
@Min(value) | 元素必须为数字,其值大于等于指定value |
@Max(value) | 元素必须为数字,其值小于等于指定value |
@Size(min,max,message) | 元素大小必须在指定范围内,message加以说明 |
@Past | 元素必须是一个过去的日期 |
@Furture | 元素必须是一个将来的日期 |
@Pattern | 元素符合指定的正则表达式 |
元素必须为电子邮箱格式 | |
@Length(min,max,message) | 必须是字符串,其值在指定范围内,message加以说明 |
@NotBlank(message) | 字符串不能为null,且trim后不能为空字符串(长度非0),message加以说明 |
@NotEmpty | 被注释的字符串、集合、数组不能为空(长度非0) |
@Range | 元素在合适的范围内 |
@SafeHtml | 元素必须是安全Html |
@URL | 元素必须是有效URL |
上述注解只是其中一部分,高亮注解是常用于Bean属性的校验,通过 @ConfigurationProperties(prefix = “user”) 引入配置文件并自动绑定属性:
User类:
package com.kdcrm.pojo;
import org.hibernate.validator.constraints.Length;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Configuration
@ConfigurationProperties(prefix = "user")
@Validated
public class User {
/**
* @notNull 限制不能为空
* @NotEmpty 元素不能为null,且不为空(字符串和集合的长度不能为0)
* @NotBlank(message = "XXX不能为空"),判断字符串是否为null,或者去掉首尾空格是否为空串
* @Length(min = 6,max = 10,message = "最短为6位,最大为10位")
* @Min(value = "6") 限制最小值
* @Max(value = "10") 限制最大值
* @Email 判断邮箱是否合法
* @NotEmpty 字符串、集合、数组不能为空(长度非0)
*/
@NotNull
@Length(min = 1,max = 3,message = "名字长度最小为1,最大为3")
private String name;
@NotNull
@Min(value = 6)
@Max(value = 16)
private String password;
@NotNull
@Size(min = 1,max = 100,message = "最小1岁,最大100岁")
private int age;
@NotBlank(message = "性别不能为空")
private String sex;
@Email
private String email;
@NotEmpty
private List list;
@NotEmpty
private Set set;
@NotEmpty
private Map map;
}
application.yml配置文件
user:
name: wbs
password: 123456
age: 21222
sex: 男男男男
email: [email protected]
list:
- dog
- cat
- pig
set:
- dog
- cat
- pig
map:
k1: zs
k2: ls
k3: ww
Controller层进行对象数据校验,用@Valid注解,通过BindingResult接收校验结果:
package com.kdcrm.controller;
import com.kdcrm.pojo.User;
import com.kdcrm.service.UserService;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.ResponseHeader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
/**
* @author 一宿君(CSDN : qq_52596258)
* @date 2021-08-06 08:52:36
*/
@Controller
public class PeopleController {
@ResponseBody
@RequestMapping("/addUser")
public ModelAndView addUser(@Valid User user, BindingResult result, ModelAndView mav){
if(result.hasErrors()){
//如果有错误,跳转至添加页面
mav.setViewName("addUser");
return mav;
}
//如果没有错误,则将数据存入到request作用域中
mav.addObject("user",user);
mav.setViewName("UserIndex");
return mav;
}
}