一、使用注解@ExceptionHandler
在Controller层中,使用@ExceptionHandler可以获取该Controller层下的异常。
@ResponseBody
//这里是说明只处理Runtime的异常
@ExceptionHandler(RuntimeException.class)
//JsonData是我封装的包含状态码和状态数据JAVA对象
public JsonData RuntimeException(RuntimeException r){
return JsonData.error(r.getMessage());
}
@GetMapping("/test")
@ResponseBody
public JsonData test(){
throw new RuntimeException("运行时异常");
}
测试接口/test
@ExceptionHandler的value值说明要捕获的异常,默认值是该Controller层下的所有Exception异常都有该异常处理。如果要捕获全局异常,则方法里面的异常需要是所有异常的父类,即Excetion类,否则会报错,因为不同的异常类并不能相互转换。
如果上述改成如下
@ResponseBody
@ExceptionHandler
//JsonData是我封装的包含状态码和状态数据JAVA对象
public JsonData RuntimeException(NullPointerException r){
return JsonData.error(r.getMessage());
}
@GetMapping("/test")
@ResponseBody
public JsonData test(){
throw new RuntimeException("运行时异常");
}
测试接口/test
程序会报异常,因为RuntimeException不能转换为NullPointerException,而ExceptionHandler捕获了所有的Exception。所以个人推荐写法是@ExceptionHandler的Value值和方法中的异常类相同,如果要捕获全部类型的异常,可以如下写法;
@ResponseBody
//这里是说明处理所有的Exception异常
@ExceptionHandler(Exception.class)
//JsonData是我封装的包含状态码和状态数据JAVA对象
public JsonData RuntimeException(Exception e){
return JsonData.error(e.getMessage());
}
二、使用 @ControllerAdvice + ExceptionHandler 实现异常处理
@ControllerAdice如果不标明特定Controller层,则所有的Controller都会被其代理通知,配合ExcetionHandler则能实现全局异常处理。
//@RestControllerAdvice等于@ResponseBody + @ControllerAdvice
@RestControllerAdvice
public class ExceptionController {
//这里是用来捕获url页面无法寻址的异常
@ExceptionHandler(Exception.class)
public JsonData exception(Exception e){
return JsonData.error(e.getMessage);
}
}
如果想限定@ControllerAdvice的作用范围,其basePackageClasses和basePackages可以进行限定。前者是直接限制某些类,后者是限制某个包下的,两者都是数组类型的,即可以限定多个类和多个包
写法如下:
@RestControllerAdvice(basePackages = {"com.test.Controller","com.ali.Controller"})
@RestControllerAdvice(basePackageClasses = {userLoginController.class,studentController.class})
以上都是SpringBoot注解提供的方法,实际上Spring自身有可以实现全局异常处理的方法。
三、继承实现HandlerExceptionResolver
这是由Spring提供的,需要编写resolveException的方法,其返回类型是ModelAndView。其格式如下:
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
由于目前流行前后端分离,使用ModelAndView已经有点不符合需求了,但是该方法response参数,于是我们可以通过response传输数据,而不依赖于ModelAndView。
@Component
public class ExceptionInterceptor implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
//说明返回数据的格式和编码类型
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
//使用fastjson工具包将数据转为JSON格式
String message = JSONObject.toJSONString(JsonData.error(ex.getMessage()));
out.println(message);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
上述方法如果需要拦截特定类型的异常,可以使用instanceof判断Exception的类型是否为该异常,如果是,才进行对应处理;
@Component
public class ExceptionInterceptor implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if(ex instanceof RuntimeRuntimeException(){
try {
//说明返回数据的格式和编码类型
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
//使用fastjson工具包将数据转为JSON格式
String message = JSONObject.toJSONString(JsonData.error(ex.getMessage()));
out.println(message);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
测试接口/test如下
具体使用哪种方法由项目架构和需求决定,当然首选是第一和第二种,写法更加优雅和灵活。