SpringBoot统一处理全局异常
注解的介绍
@ControllerAdvice
@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。
这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。
2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。
3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。
从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。针对声明@ExceptionHandler 、 @InitBinder或@ModelAttribute方法的类的@Component @ExceptionHandler , @InitBinder在多个@Controller类之间共享。
使用@ControllerAdvice注解的类可以明确声明为 Spring bean 或通过类路径扫描自动检测。 所有此类 bean 都根据Ordered语义或@Order / @Priority声明进行Ordered , Ordered语义优先于@Order / @Priority声明。 然后在运行时按该顺序应用@ControllerAdvice bean。 但是请注意,实现PriorityOrdered @ControllerAdvice bean 的PriorityOrdered不高于实现Ordered @ControllerAdvice bean。 此外, Ordered不适用于范围内的@ControllerAdvice例如,如果这样的 bean 已被配置为请求范围或会话范围的 bean。 对于处理异常, @ExceptionHandler将在第一个具有匹配异常处理程序方法的通知中被选择。 对于模型的属性和数据绑定初始化, @ModelAttribute和@InitBinder方法将遵循@ControllerAdvice秩序。
注意:对于@ExceptionHandler方法,在特定建议 bean 的处理程序方法中,根异常匹配将优先于仅匹配当前异常的原因。 但是,与较低优先级建议 bean 上的任何匹配(无论是根还是原因级别)相比,更高优先级建议上的原因匹配仍然是首选。 因此,请在具有相应顺序的优先建议 bean 上声明您的主要根异常映射。
默认情况下, @ControllerAdvice ControllerAdvice 中的方法全局应用于所有控制器。 使用诸如annotations 、 basePackageClasses和basePackages (或其别名value )之类的选择器来定义目标控制器的更窄子集。 如果声明了多个选择器,则应用布尔OR逻辑,这意味着所选控制器应至少匹配一个选择器。 请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。
@ExceptionHandler拦截异常并统一处理
配合 @ExceptionHandler注解结合使用,当异常抛到controller层时,可以对异常进行统一的处理,规定返回的json格式或者跳转到指定的错误页面等.
@ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。
用于处理特定处理程序类和/或处理程序方法中的异常的注解。
使用此注解注释的处理程序方法允许具有非常灵活的签名。 它们可能具有以下类型的参数,按任意顺序排列:
异常参数:声明为一般异常或更具体的异常。 如果注解本身没有通过其value()缩小异常类型,这也可用作映射提示
代码实现
自定义异常/** * 自定义一个异常类,用于处理我们发生的业务异常 * * @author Promsing(张有博) * @version 1.0.0 * @since 2021/11/27 - 20:14 */ public class BizException extends RuntimeException { private static final long serialVersionUID = 1L; /** * 错误码 */ protected String errorCode; /** * 错误信息 */ protected String errorMsg; public BizException() { super(); } public BizException(FrontResult errorInfoInterface) { super(errorInfoInterface.getCode()); this.errorCode = errorInfoInterface.getMessage(); this.errorMsg = errorInfoInterface.getMessage(); } public BizException(FrontResult errorInfoInterface, Throwable cause) { super(errorInfoInterface.getCode(), cause); this.errorCode = errorInfoInterface.getCode(); this.errorMsg = errorInfoInterface.getMessage(); } public BizException(String errorMsg) { super(errorMsg); this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg) { super(errorCode); this.errorCode = errorCode; this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg, Throwable cause) { super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public String getMessage() { return errorMsg; } @Override public Throwable fillInStackTrace() { return this; } }
统一异常处理
import com.tfjy.arbackend.enumtool.ResultCodeEnum; import com.tfjy.arbackend.enumtool.ResutlMsgEnum; import com.tfjy.arbackend.util.FrontResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.sql.SQLException; /** * 统一异常处理 * * @author Promsing(张有博) * @version 1.0.0 * @since 2021/11/27 - 20:14 */ @ControllerAdvice//使用该注解表示开启了全局异常的捕获 public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 处理自定义的业务异常 * @param req * @param e * @return */ @ExceptionHandler(value = BizException.class) @ResponseBody public FrontResult bizExceptionHandler(HttpServletRequest req, BizException e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("发生业务异常!原因是:{}",e.getErrorMsg()); return FrontResult.getExceptionResult(e.getErrorCode(),e.getErrorMsg()); } /** * 处理空指针的异常 * @param req * @param e * @return */ @ExceptionHandler(value =NullPointerException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("发生空指针异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 处理索引越界异常 * @param req * @param e * @return */ @ExceptionHandler(value =IndexOutOfBoundsException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("索引越界异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 处理类未找到异常 * @param req * @param e * @return */ @ExceptionHandler(value =ClassNotFoundException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("发生类未找到异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 处理SQL异常 * @param req * @param e * @return */ @ExceptionHandler(value = SQLException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, SQLException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("发生SQL异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 处理IO异常 * @param req * @param e * @return */ @ExceptionHandler(value = IOException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IOException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("发生IO异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 处理其他异常 * @param req * @param e * @return */ @ExceptionHandler(value =Exception.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, Exception e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("未知异常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } }
前端返回值类
import com.tfjy.arbackend.enumtool.ResultCodeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class FrontResult { /** * 结果状态码 */ private String code; /** * 响应结果描述 */ private String message; /** * 返回数据 */ private Object data; /** * 静态方法,返回前端实体结果 * * @param code 状态码 * @param message 消息 * @param data 数据 * @return 前端实体结果 */ public static FrontResult build(String code, String message, Object data) { return new FrontResult(code, message, data); } /** * 返回成功的结果实体 * * @param message 消息 * @param data 数据 * @return 实体 */ public static FrontResult getSuccessResult(String message, Object data) { FrontResult result = new FrontResult(); result.code = ResultCodeEnum.SUCCESS.getCode(); result.message = message; result.data = data; return result; } /** * 返回无需data的成功结果实体 * * @param message 消息内容 * @return 返回结果 */ public static FrontResult getSuccessResultOnlyMessage(String message) { FrontResult result = new FrontResult(); result.code = ResultCodeEnum.SUCCESS.getCode(); result.message = message; result.data = null; return result; } /** * 获取一个异常结果 * * @param code 错误码 * @param message 自定义异常信息 * @return FrontResult */ public static FrontResult getExceptionResult(String code, String message) { FrontResult result = new FrontResult(); result.code = code.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getCode() : code; result.message = message.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getMsg() : message; return result; } }import lombok.AllArgsConstructor; @AllArgsConstructor public enum ResultCodeEnum { // 请求成功 SUCCESS("0000"), // 请求失败 FAIL("1111"), // EXCEL 导入失败 EXCEL_FAIL("1000"), // userID 为空 ID_NULL("1001"), // 前端传的实体为空 MODEL_NULL("1002"), // 更新失败 UPDATE_FAIL("1011"), // 参数为空 PARAM_ERROR("400"), // 代码内部异常 CODE_EXCEPTION("500", "代码内部异常"); /** * 状态码 */ private String code; public String getCode() { return code; } ResultCodeEnum(String code) { this.code = code; } private String msg; public String getMsg() { return msg; } } public enum ResutlMsgEnum { //查询成功 FIND_SUCCESS("查询成功!"), //查询失败 FIND_FAIL("查询失败!"), //更新成功 UPDATE_SUCCESS("更新成功"), //更新失败 UPDATE_FAIL("更新成功"), SEND_SUCCESS("发送成功"), SEND_FAIL("发送失败"); private String msg; ResutlMsgEnum(String msg) { this.msg = msg; } public String getMsg() { return msg; } }
测试用例/** * 测试用例 * * @author Promsing(张有博) * @version 1.0.0 * @since 2021/11/29 - 9:05 */ @Api(tags = {"测试controller"}) @RequestMapping(value = "/testController") @RestController public class TestController { @ApiOperation(value = "测试null") @GetMapping(value = "getNull") public FrontResult getNull() { int length = 0; String name=null; length = name.length(); return FrontResult.build(ResultCodeEnum.SUCCESS.getCode(), ResutlMsgEnum.EXECUTE_SUCCESS.getMsg(), length); } }
其他异常同理,也可以捕获。完美,没问题。全局统一异常处理设置成功。
有没有什么帮助学习的app?有时候,想要投入学习,提高效率需要借助一些工具。像是一些记性不好的同学,想要进行时间管理,最好还是把计划要做的事情列一个清单。今天范叔给你们介绍一些我自己正在用的实用APP,希望能
小米千元5G突然降价一亿像素高刷立体双扬,性价比首选众所周知,2021年千元机价位已经发布了很多优质的产品,而此前发布的一些千元机价格也出现了松动,近期小米旗下的RedmiNote9pro也传来了好消息,闪降100元,标准版6128
荣耀x10麒麟820还值得入手吗?今天有人问我今年还可以入手荣耀x10吗?现在这款手机是什么样的性能?值不值?从今年的千元机市场来看,如今的千元机无论从性能质量外观都已经非常上档次了,完全可以满足日常生活需要,不管
iPhone12狂降价超千元,还不买?如果要选出一家最懂用户心思的手机厂商,个人认为苹果绝对有机会上榜,特别是降价一招用的更是炉火纯青,很多人甚至怀疑当年iPhone11比XR起售价低1000元是苹果早就计划好的,以至
24小时血压监测didoE40SPro顶配版试用哈喽哈喽,大家好,我是超级Leo。在上次试用完didoY2之后,Leo对其品牌和产品还是蛮肯定的。这次有借着家里另外一个刚需,老人的血压测量功能,试用了其didoE40SPro顶配
28号,华为将举行新品发布会华为最近陆续会有多场发布会,但是都异常低调,今天也有一场终端商用办公新品发布会,都没有宣传,等发布完再为大家讲解。28号还有一场华为新品发布会第一会为大家带来新的外折方案的华为折叠
发布至今下跌3200元,12GB256GB,2K全视屏旗舰无奈依然少人问津以前手机屏幕上最重要的参数是分辨率,但是现如今手机屏幕上最重要的参数却已经是变成了刷新率,消费者们选购手机的时候,分辨率不高没事,但一定要支持高刷,这是因为高刷新率确实会非常直观的
白话云计算边缘计算大数据云计算边缘计算大数据这些概念总给人以高端神秘的感觉,有些商家还以此为噱头,营销产品,忽悠消费者,把消费者弄得云里雾里。所谓云计算,字面上理解就是在云上做计算,云给人直观的特点有在远
换电和加油哪个更贵?答案超乎所有人的想象!我想很多人买电动车的一大原因就是因为它省钱,一般来说,哪怕使用公共充电桩,一公里成本也可以压在2毛以内。作为对比,普通燃油车按照8L100km的油耗来计算并且加注92汽油的话,一公
2022年1000元以内价位手机选购推荐当前1000元以内价位手机中值得推荐的产品主要集中在RedmiiQOO荣耀等几个品牌,而且售价主要集中在999元价位。至于其他手机品牌,要么因为利润太低选择放弃1000元以内价位市
拼多多启动多多读书月第三季亿元级补贴助力知识普惠进山区与拼多多一起,将知识普惠进行到底。4月20日,拼多多正式启动第三季多多读书月活动,持续时间为1个月。在第一季和第二季多多读书月活动的基础上,第三季读书月拼多多继续加大补贴力度扩大书