总体来讲,SpringBoot 处理异常有下面几个。
一、利用 SpringBoot 的默认配置,我们自定义覆盖默认的错误页面:1、SpringBoot 默认错误处理机制
SpringBoot 为我们做了一堆的默认配置工作整合,也包含异常处理的配置
如果没有进行处理出现错误,如果是浏览器访问,它会返回一张 html 的错误页面,如果是其他客户端访问出现异常,它会直接返回json格式的异常数据。
1)在ErrorMvcAutoConfiguration中,重点看一下这三个:SpringBoot 错误处理机制源码跟踪
(1)查看 BasicErrorController 类,默认处理/error请求的。
其中有两个方法处理错误请求:
返回的异常信息可以在 DefaultErrorAttributes 类中查询
(2)DefaultErrorAttributes 封装了默认错误数据
(3)DefaultErrorViewResolver 默认错误视图解析器
结论:
1. 有模板引擎的情况下
error/状态码 -- 将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的error文件夹下(/templates/error/xxx.html),发生此状态码的错误就会来到 对应的页面。使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确状态码优先。
2. 没有模板引擎(模板引擎找不到这个错误页面)
SpringBoot会去静态资源目录(static/error/状态码.html)下面找错误代码命名的页面。
3. 完整的错误页面查找方式应该是:比如发生了500错误
查找500.html 页面(模板引擎)–>查找静态 500.html –> 查找 5xx.html(模板引擎)–>查找静态 5xx.html。
以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面。
2、自定义错误页面覆盖默认的:4xx 客户端错误,5xx 服务端错误
3、自定义异常数据
默认情况下异常数据会展示出5条数据(具体在DefaultErrorAttributes 类的 getErrorAttributes 方法中定义的)。
如果开发者没有自己提供一个 ErrorAttributes 的实例的话,那么 Spring Boot 将自动提供一个ErrorAttributes 的实例,也就是 DefaultErrorAttributes 。开发者自定义 ErrorAttributes 有两种方式 :
- 直接实现 ErrorAttributes 接口(不推荐)
- 继承 DefaultErrorAttributes类(推荐),因为 DefaultErrorAttributes 中对异常数据的处理已经完成,开发者可以直接使用。
这里创建一个类继承 DefaultErrorAttributes类自定义异常数据:
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map map = super.getErrorAttributes(webRequest, options);
if((Integer) map.get("status") == 500){
map.put("myMessage", "500-服务器内部错误!");
}
return map;
}
}
4、自定义异常视图
异常视图默认就是前面所说的静态或者动态页面(在BasicErrorController 类的 errorHtml 方法中处理的,如果是 ajax 请求,则是在error方法处理)。
这也是可以自定义的:开发者提供了自己的 ErrorViewResolver 实例,重新定义一个 ModelAndView 即可。
注意:参数中的 model 类型为 UnmodifiableMap,即不可以直接修改,它里面是封装的错误数据。在这里自定义一个map,遍历model数据拷贝过去并修改到map中,也是可以实现数据自定义的。
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
super(applicationContext, resourceProperties);
}
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) {
ModelAndView modelAndView = new ModelAndView("/diy/1234", model);
return modelAndView;
}
}
二、某个异常单独处理 @ExceptionHandler注解
某个异常处理主要用到 @ExceptionHandler 注解,此注解加到类的方法上,当此注解里定义的异常抛出时,该方法会被执行。
如果 @ExceptionHandler 所在的类是 @Controller 注解,则此方法只作用在此controller类中。
如果 @ExceptionHandler 所在的类是 @ControllerAdvice 注解,则此方法会作用在全局。
1、某个controller处理: @Controller + @ExceptionHandler
@Controller
public class UserController {
@GetMapping("/userList")
public String userList(Model model){
int i = 1/0;
List userList = userService.getAllUser();
model.addAttribute("userList", userList);
return "userList";
}
//局部异常处理 - 页面
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView exHandler(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("err", ex.getMessage()); //更加自由显示处理异常
modelAndView.setViewName("/myError");
return modelAndView;
}
/* //局部异常处理 - JSON格式
@ExceptionHandler(value = {java.lang.Exception.class})
@ResponseBody
public String exHandler(Exception e){
// 判断发生异常的类型是除0异常则做出响应
if(e instanceof ArithmeticException){
return "发生了除0异常";
}
// 未知的异常做出响应
return "发生了未知异常";
}*/
}
2、全局异常处理:@ControllerAdvice + @ExceptionHandler
在spring 3.2中,新增了 @ControllerAdvice 注解,可以用于定义 @ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。
简单的说,进入Controller层的错误才会由 @ControllerAdvice 处理,拦截器抛出的错误以及访问错误地址的情况 @ControllerAdvice 处理不了,由SpringBoot默认的异常处理机制处理。
@ControllerAdvice
public class DoAllErrotController {
//全局异常处理 - 视图
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView exHandler(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("err", ex.getMessage()); //更加自由显示处理异常
modelAndView.setViewName("/myError");
return modelAndView;
}
//全局异常处理 - JSON格式
/*
@ExceptionHandler(value = {RuntimeException.class})
@ResponseBody
public Map errorJson(){
Map errorMap = new HashMap();
errorMap.put("errorCode", "500");
errorMap.put("errorMessage", "发送异常啦!");
return errorMap;
}
*/
}
某个controller处理和全局异常处理同时处理时, 先某个,某个处理没有回再全局处理。
3、使用@ControllerAdvice定义全局数据
如果有个需求,想在所有的Controller类中获取某个或者多个全局的数据,可以使用@ControllerAdvice定义全局数据。
@ControllerAdvice
public class MyGlobalData {
// 自定义kv。key - globalKey1,v - Map。
@ModelAttribute(value = "globalKey1")
public Map globalData(){
Map map = new HashMap();
map.put("aaa","vvv1");
map.put("bbb","vvv2");
return map;
}
}
@Controller
public class ErrorController {
@GetMapping("/get")
@ResponseBody
public String get(Model model){
Map map = model.asMap();
map.forEach((k,v) -> {
System.out.println(k);
System.out.println(v);
});
return "success";
}
}
在@Configuration注解的配置类中,注入SimpleMappingExceptionResolver,bean配置来处理做全局异常处理,但是这种方法不能返回异常的具体信息。
@Configuration
public class MyErrorConfig {
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.put("java.lang.NullPointerException","myError"); //参数:异常类型,视图名称
properties.put("java.lang.ArithmeticException","myError");
simpleMappingExceptionResolver.setExceptionMappings(properties); //设置异常与视图映射信息的,默认通过${exception} 获取,可修改
simpleMappingExceptionResolver.setExceptionAttribute("err"); //
return simpleMappingExceptionResolver;
}
}
注入bean的配置和@ControllerAdvice方式同时使用时,优先是@ControllerAdvice方式处理。
之前SpringMVC 的配置:
需 要 再 全 局 异 常 处 理 类 中 实 现 HandlerExceptionResolver 接口,并添加@Configuration注解。
@Configuration
public class MyErrorConfig5 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if(e instanceof ArithmeticException){
modelAndView.setViewName("myError");
}
if(e instanceof NullPointerException){
modelAndView.setViewName("error");
}
modelAndView.addObject("err",e);
return modelAndView;
}
}
项目中根据情况,合理选择上面的处理方式。
ends~