全局异常
我们知道在web开发中,我们要处理全局异常。全局异常在web又分为:已知异常,未知异常。
已知异常:就是我们能处理的,俗称BUG。
未知异常:就是开发者不能处理的异常,要用日志记录下来。
而在SpringBoot中异常处理,SpringBoot已经提供了
就是@ControllerAdivce 这个注解
我们来看下这个注解的源码
package org.springframework.web.bind.annotation; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {}; }
这个注解只能作用类上
@ControllerAdvice
是一个特殊的@Component
,用于标识一个类,这个类中被以下三种注解标识的方法:@ExceptionHandler
,@InitBinder
,@ModelAttribute
,将作用于所有的@Controller
类的接口上@InitBinder的作用
作用:注册属性编辑器,对HTTP请求参数进行处理,再绑定到对应的接口,比如格式化的时间转换等。应用于单个@Controller类的方法上时,仅对该类里的接口有效。与@ControllerAdvice组合使用可全局生效
@ControllerAdvice public class ActionAdvice { @InitBinder public void handleException(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); } } 作者:空夜 链接:https://juejin.im/post/6844903826412011533 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
@ModelAttribute
作用:绑定数据
@ControllerAdvice public class ActionAdvice { @ModelAttribute public void handleException(Model model) { model.addAttribute("user", "zfh"); } } 作者:空夜 链接:https://juejin.im/post/6844903826412011533 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在接口中获取前面绑定的参数:
@RestController public class BasicController { @GetMapping(value = "index") public Map index(@ModelAttribute("user") String user) { //... } } 作者:空夜 链接:https://juejin.im/post/6844903826412011533 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
@ExceptionHandler
作用:统一异常处理,也可以指定要处理的异常类型
来看一下源码
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExceptionHandler { /** * Exceptions handled by the annotated method. If empty, will default to any * exceptions listed in the method argument list. */ Class<? extends Throwable>[] value() default {}; }
Class<? extends Throwable>[] value() default {}; 是泛型类数组 继承 Throwable
现在让我们回到java中的异常
Error
表示严重的错误,程序对此一般无能为力,出现Error异常程序都不能运行了,还处理个毛。
所以在SpringBoot中全局处理异常Exception。
Exception 分为 checkedException 和 RuntimeException
Checked Exception: 指的是不能恢复,必须要被使用者来处理的一类异常,如果不捕获,那么编译会报错。例如,IOException。
Unchecked Exception: 指的是在运行时才会导致程序奔溃的异常,编译时候并不会报错。例如,Runtime Exception
所以在web中已知异常HttpException 通常继承RuntimeException异常。
package com.lin.missyou.core; import com.lin.missyou.core.configuration.ExceptionCodeConfiguration; import com.lin.missyou.exception.http.HttpException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; 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.bind.annotation.ResponseStatus; import javax.servlet.http.HttpServletRequest; /** * 全局异常处理 */ @ControllerAdvice public class GlobalExceptionAdvice { @Autowired private ExceptionCodeConfiguration codeConfiguration; //未知异常处理 @ExceptionHandler(value = Exception.class) @ResponseBody @ResponseStatus public UnifyResponse handleException(HttpServletRequest request,Exception e){ String errorUrl =request.getMethod()+ " " +request.getRequestURL(); UnifyResponse unifyResponse = new UnifyResponse(9999,"服务器未知错误",errorUrl); return unifyResponse; } //已知异常处理 @ExceptionHandler(value = {HttpException.class,}) public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest request, HttpException e){ String errorUrl =request.getMethod()+ " " +request.getRequestURI(); String message = codeConfiguration.getMessage(e.getCode()); UnifyResponse unifyResponse = new UnifyResponse(e.getCode(),message,errorUrl); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); HttpStatus status = HttpStatus.resolve(e.getHttpStatusCode()); ResponseEntity<UnifyResponse> responseEntity = new ResponseEntity<>(unifyResponse,httpHeaders,status); return responseEntity; } }
package com.lin.missyou.core; /** * 统一错误返回 */ public class UnifyResponse { private int code; private String message; private String request; public UnifyResponse(String request) { this.request = request; } public UnifyResponse(int code, String message, String request) { this.code = code; this.message = message; this.request = request; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getRequest() { return request; } public void setRequest(String request) { this.request = request; } }
在上面代码中要解释的注解@ResponseStatus
这里主要就是改变响应体的状态码 例如:500 服务器错误
这里的HttpException 是自定义的继承RuntimeException
package com.lin.missyou.exception.http; public class HttpException extends RuntimeException { protected int code; protected int httpStatusCode=500; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public int getHttpStatusCode() { return httpStatusCode; } public void setHttpStatusCode(int httpStatusCode) { this.httpStatusCode = httpStatusCode; } }