文章目录
入参中的异常处理
方法内的异常,手动打日志很容易。
入参发生异常,接口肯定是返回信息的。 但是之后排查很有困难,因为根本没有记录日志。
解决方案:
使用异常拦截器 @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、如果在一个类中没有匹配到异常,会继续到下一个类中查找。
注:建议一个项目只有一个异常处理类,便于管理。