入参中的异常处理(全局异常处理)

入参中的异常处理

方法内的异常,手动打日志很容易。
入参发生异常,接口肯定是返回信息的。 但是之后排查很有困难,因为根本没有记录日志。

解决方案:
使用异常拦截器 @ExceptionHandler和@ControllerAdvice,这里不叙述使用方法。

400异常模拟

@RequestParam(required = true)设置入参必填

@RequestParam(required = true) 设置入参username为必填,为空报错:

@RequestMapping("/queryString")
@ResponseBody
public void queryString(@RequestParam(required = true) String username){
    System.out.println(username);
}

@RequestBody @Valid 设置入参校验

 @RequestMapping("/query")
 @ResponseBody
 public void query(@RequestBody @Valid User user){
     System.out.println("query");
 }

为什么2个注解都需要

@RequestBody的required默认为true,所以@RequestBody 和 @RequestBody(required=true) 一样。

那么为什么只设置@RequestBody(required=true) 即使入参为空也不报错?
是这样的, 如果入参为空,那么仍然创建了一个User对象,只不过属性都是空字符串。

所以需要加上@Valid,检验User类的属性。

User类代码

@Data
public class User {
    @NotBlank(message = "姓名不能为空")
    private String username;
    private String password;
}

入参中的脏代码

脏代码带来的入参问题。
java代码:

private Integer Id;
public Integer getId() {
    return this.Id;
}

public void setId(Integer id) {
    this.Id = id;
}

报文入参:
{
“Id”:“1234”
}

结果报错了,参数映射失败,根本进入不到方法里。

原因:
拼报文的时候一般直接根据属性来写,这里属性是 Id,
但是入参调用的是set方法,set方法用的是 id,所以根本找不到这个属性,报错了。

所以,良好的代码习惯可以避免很多意想不到的错误。

全局异常处理

全局异常处理-示例

@ControllerAdvice 和@ExceptionHandler(Exception.class) 实现异常处理。

@ControllerAdvice
public class GlobalException {
    private static Logger logger = LoggerFactory.getLogger(GlobalException.class);

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public JsonResult handleException(Exception e){
        logger.error("error:",e);
        return null;
    }
}

异常处理的其他思考

经常遇到的问题,common包里面有全局异常处理。 我想写自定义的,这样会冲突吗?
咱们一点一点的看。

可以有多个异常处理类吗、都有效吗

先做个试验。
异常处理类1:

@ControllerAdvice
public class GlobalException {
    private static Logger logger = LoggerFactory.getLogger(GlobalException.class);

    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public void handleException(Exception e){
        logger.error("NullPointerException error:",e);
    }
}

异常处理类2:

@ControllerAdvice
public class GlobalException2{
    private static Logger logger = LoggerFactory.getLogger(GlobalException2.class);

    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public void handleException(Exception e){
        logger.error("ArithmeticException error:",e);
    }
}

然后模拟下2个异常:

// 触发 NullPointerException 
String s=null;
System.out.println(s.indexOf(1));

// 触发 ArithmeticException 
System.out.println(0/0);

发现不同异常,是由不同的处理类来完成的,说明都生效了。

那么问题来了? 多个异常处理类的机制是什么,优先级怎样

多个异常处理类的机制、顺序/优先级

所有@ControllerAdvice类都会被注册为一个列表,根据@Order来排序。 如果没有@Order注解,默认都是最低的优先级,会按照注册顺序来排序。

当一个异常发生时,会在列表找到优先级最高的异常处理类,在该类中查找是否有匹配的异常处理,有的话进入对应方法。 没有的话,继续查找下一个类。

如果想让一个类的优先级最高,设置最高优先级即可,代码:

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalException {
}

注: Ordered是数值越小,排名越靠前。

@ControllerAdvice注解的类可以继承吗

还是做试验。
代码:

@ControllerAdvice
public class GlobalException2 extends GlobalException{
    private static Logger logger = LoggerFactory.getLogger(GlobalException2.class);

    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public void handleException(Exception e){
        logger.error("ArithmeticException error:",e);
    }
}

发现是可以的。不过貌似没啥用,因为我直接注册个新类不就行了,没必要用继承。
注: 子类一定要有@ControllerAdvice注解,否则注册不到异常列表的。

扩展全局异常处理

回到最初的问题,jar包中已经有全局异常处理类了。 如何写自定义的异常处理类。

解决方案

方案一: 在本项目中新建相同包名类名的异常处理类。优先级更高,直接覆盖依赖包中的类了。想怎么改都可以。

方案二: 新建一个@ControllerAdvice,并设置@Ordered为高优先级,这样本类优先处理异常。

这两种方案都是可以的。

错误的解决方案

方案一: 新建一个自定义异常,并新建一个@ControllerAdvice但不设置高优先级,代码中抛出自定义异常,已有的异常处理类匹配不上这个异常,所以会进入新建类的异常处理。
错。 思路看上去也没问题,但是忽略了一点,全局异常一般都有Exception通用异常处理,所以即使没有精确匹配到,也会被Exception拦下来,根本到不了新建的异常类。

总结

1、所有@ControllerAdvice类都会被注册到列表中,默认都是最低优先级。
2、@Ordered可以设置优先级,优先级高的类先处理异常。
3、如果在一个类中没有匹配到异常,会继续到下一个类中查找。

注:建议一个项目只有一个异常处理类,便于管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值