springboot全局异常处理

1、spring提供了一套控制全局异常的注解配置,通过使用注解,可以给异常一个全局出口,使用@RestControllerAdvice或者@ControllerAdvice(@RestControllerAdvice相当于@ControllerAdvice和@ResponseBody的组合)

2、@ControllerAdvice:是controller的一个辅助类,最常用的就是作为全局异常处理的切面类,可以指定扫描范围

3、@ExceptionHandler:指定异常类的处理方式

5、示例:

@RestController
@RequestMapping("/api/hello")
public class HelloWorldController {
	@PostMapping("/test1")
    public BaseResult exceptionTest1() throws Exception {
        throw new Exception("Exception。。。。。");
    }

    @PostMapping("/test2")
    public BaseResult exceptionTest2() throws MyException {
        throw new MyException("MyExcepton....");
    }
}
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public BaseResult exceptionHandler(Exception ex){
        log.info("1111111111111");
        return BaseResult.failed("1000",ex.getMessage());
    }

    @ExceptionHandler(MyException.class)
    public BaseResult myExceptionHandler(MyException ex){
        log.info("2222222222222");
        return BaseResult.failed("1000",ex.getMessage());
    }
}

当访问/api/hello/test1接口时,因为接口抛出的是Exception,所以被exceptionHandler处理,返回"1111111111111"。
当访问/api/hello/test2接口时,因为接口抛出的是MyException,所以被myExceptionHandler处理,返回"2222222222222"。

6、@ControllerAdvice只是处理Controller层的异常,但是如果请求还没到controller层就抛异常了(比如404),这种情况下,使用@ControllerAdvice是没法处理的,下面看看spring全局异常处理的逻辑:

  • 在spring中全局异常处理的核心是ErrorController接口,该接口中只提供了一个getErrorPath()方法
  • AbstractErrorController实现了ErrorController接口
  • 全局异常具体的处理映射逻辑都在BasicErrorController中,它继承了AbstractErrorController
    • 在BasicErrorController中提供了errorHtml()和error()方法,前者响应的是一个错误页面,后者响应的是json数据
/**BasicErrorController核心代码*/

@RequestMapping(produces = "text/html")
	public ModelAndView errorHtml(HttpServletRequest request,
			HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
				request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}
如果返回的是一个错误页面,执行了resolveErrorView方法,该方法中调用了DefaultErrorViewResolver类的resolveErrorView方法
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
		String errorViewName = "error/" + viewName;
		//如果有可用的模板(在error文件夹下),则使用模板展示,
		//如果没有可用的模板,则查找静态资源文件(static文件夹下)
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
		if (provider != null) {
			return new ModelAndView(errorViewName, model);
		}
		return resolveResource(errorViewName, model);
	}

	private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
		for (String location : this.resourceProperties.getStaticLocations()) {
			try {
				Resource resource = this.applicationContext.getResource(location);
				resource = resource.createRelative(viewName + ".html");
				if (resource.exists()) {
					return new ModelAndView(new HtmlResourceView(resource), model);
				}
			}
			catch (Exception ex) {
			}
		}
		return null;
	}

经过上面的分析,如果我们想处理类似404场景的异常,就需要自定义自己的异常处理类。实现ErrorController接口,然后提供error()或者errorHtml()方法

@RestController
@Slf4j
public class MyBasicErrorController implements ErrorController {

    @Autowired
    private ErrorAttributes errorAttributes;

    @Value("${server.error.path:${error.path:/error}}")
    private static final String path_default = "/error";

    @Override
    public String getErrorPath() {
        return path_default;
    }

    @RequestMapping(value = path_default ,produces = {MediaType.APPLICATION_JSON_VALUE})
    public BaseResult error(HttpServletRequest request) {
        RequestAttributes requestAttributes = new ServletRequestAttributes(request);
        Map<String, Object> body = this.errorAttributes.getErrorAttributes(requestAttributes, true);
        log.info("错误信息:{}",JSON.toJSON(body));
        return BaseResult.failed(body.get("status").toString(),body.get("message").toString());
    }
}

这样双管齐下,所有异常返回的数据都有一个统一的数据格式

ps:其实也可以直接继承抽象类AbstractErrorController,但是启动服务的时候总是报一个很奇怪的错误,有时间继续研究一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值