基本介绍
在Springboot里面,提供了一套完善的异常处理机制,使用过springboot的同学应该都体会过
在我们没有使用Springboot之前,当我们访问一个不存在的地址时,显示的是浏览器的默认404页面
但是当我使用了springboot以后,当我们访问一个不存在的页面时
显示的却是一个springboot提供的默认错误视图
异常处理的使用
在Springboot中提供了两个注解来帮助我们规划异常,一个是 @ExceptionHandler,另外一个是@ControllerAdvice
@ExceptionHandler介绍
由上面可以看出来这个注解适用的对象只有方法,就是说他只能声明在方法上
再一个就是他的参数类型,他只能传入一个class类型的参数,而这个class类型的参数就是他所接管的异常类型
可以看出来这个注解是一个专门用于处理异常的注释
那么
总的来说:他只能声明在方法上面,他可以处理他所接管的异常类型
@Controller
public class error01 {
@GetMapping("/error")
public String error(){
int[] arr=new int[3];
//在这里会抛出一个数组角标越界的异常,然后会被下面exception()接收处理
System.out.println(arr[4]);
return "ok";
}
@ExceptionHandler({ArrayIndexOutOfBoundsException.class})
public String exception(){
System.out.println("局部异常。。。。");
return "error";
}
}
结果
经过测试也可以知道,他的作用域仅限当前类,出了当前类该异常处理方法就失效了
@ControllerAdvice的使用
首先他的使用对象是“TYPE”,那就代表着他只能添加到类或者接口上
而且他也是被@Component所修饰,那么就说明被他修饰的类也会被注入到Spring容器中
由于他的官方介绍很臭很长
直接总结:他的作用是:将一个类声明为全局异常类,@ControllerAdvice + @ExceptionHandler配合使用,可以将当前类中的所有被 @ExceptionHandler 修饰的方法声明为全局异常处理方法
@Controller
public class error01 {
@GetMapping("/error2")
public String error2(){
int a=1/0;
return "ok";
}
}
@ControllerAdvice
public class globalError {
@ExceptionHandler({ArithmeticException.class})
public String error(){
System.out.println("全局异常。。。。");
return "error";
}
}
结果
局部异常处理与全局异常处理的区别
- 两者的异常处理方法都是需要被@ExceptionHandler 修饰,不同的地方是全局异常处理所处的类需要被@ControllerAdvice修饰
- 作用域问题:局部异常处理 作用域在当前类 ,而全局异常处理 作用域在当前会话
- 优先级问题:
@Controller
public class error01 {
@GetMapping("/error2")
public String error2(){
int a=1/0;
return "ok";
}
@ExceptionHandler({ArrayIndexOutOfBoundsException.class,ArithmeticException.class})
public String exception(){
System.out.println("局部异常。。。。");
return "error";
}
}
@ControllerAdvice
public class globalError {
@ExceptionHandler({ArithmeticException.class})
public String error(){
System.out.println("全局异常。。。。");
return "error";
}
}
结果
由此可以看出,我设计的是,全局处理与局部处理都可以接管ArithmeticException的处理,当我们抛出一个ArithmeticException时,可以发现他是被局部异常接管
那么就可以得出结论:局部异常处理的优先级高于全局异常处理的优先级
自定义异常
为什么要有自定义异常?
因为java提供的默认异常不能满足我们所有的需求
就比如说一个添加地址的功能,我们约定一个账号最多只能添加5条地址,那么当我们超过5条时就得报错,但是报什么错的?exception?这就很不利用开发人员定位错误位置。这时候自定义异常就能很好的解决这个问题
怎么实现
Springboot也提供了一个@ResponseStatus注解,帮助我们实现
//通过code 定义异常状态码,通过reson定义异常信息
@ResponseStatus(code = HttpStatus.BAD_REQUEST,reason ="123")
public class myError extends RuntimeException{
public myError() {
}
public myError(String message) {
super(message);
}
}
@Controller
public class error01 {
@GetMapping("/error3")
public String error3(){
throw new myError("自定义异常");
}
@ExceptionHandler({ArrayIndexOutOfBoundsException.class,ArithmeticException.class,myError.class})
public String exception(Throwable t){
System.out.println("局部异常。。。。");
System.out.println(t.getMessage());
return "error";
}
}
自定义异常与内置异常一样,可以被异常处理方法接管(一视同仁)
自定义异常页面
Springboot的异常处理机制还有一个很强大地方就是他允许用户自定义异常页面,就是我们可以自己定义比如出现了404要显示什么页面,出现500显示什么页面
接下来就模拟一个500错误
先说结论
最终肯定是报一个500的异常,显示对应的页面呢,首先就会去类路径下的static/error/500.html,如果找到就显示该页面,如果没有就是退而求其次去找类路径下的static/error/5xx.html 如果找到了也返回,如果没有就返回springboot默认错误视图
下面是需要用到的代码
//需要注册一个拦截器(mvcconfiguration没放)
@Component
public class interceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
System.out.println("uri = " +uri);
return true;
}
}
//模拟一个500错误
@Controller
public class Err {
@GetMapping("/err2")
public String error2(){
int a=1/0;
return "ok";
}
}
因为我们注册了拦截器,那么我们访问/err2这个地址就会被拦截器拦截
然后就到了执行业务方法,在这里会抛出一个ArithmeticException
接下来问题就来了,异常出现以后拦截器又拦截到了一个请求,为什么访问一个 /error ?
因为我们上面说到了,用户可以自定义异常页面,而按道理来说,需要一个“人”,也就是一个对象来完成这个工作,而这个对象就是/error,通过他对应controller来开启异常页面的选择功能
最终请求会走到DefaultErrorViewResolver类的resolve()
在这里会接收到了异常信息,因为我们模拟的是一个500的异常,这里也接收到了对应信息
最终会匹配到static/ error/ 500.html(这个页面是我们已经存在的情况下才会走到这)
显示的与我们定义的页面一致
接下来模拟一个404异常
由于我没有配置一个404的页面,那么他就退而求其次,找到了一个4xx.html的视图
接下来再模拟404,不过把所有404.html,4xx.html都删除掉
他经过了两轮匹配,404.html与4xx.html都匹配不到
那么就是显式Springboot默认视图