一、Throwable
所有异常类的父类,下面分为Error和Exception两个子类
Error表示系统错误或资源耗尽,由Java系统自己使用,应用程序不应抛出和处理
Exception表示应用程序错误,它有很多子类,应用程序也可以通过继承Exception或其子类创建自定义异常
二、 异常分类
未受检异常:运行时异常RunningTimeException、Error及其子类也是未受检异常
受检异常:Exception的其他子类和Exception自身,
受检异常,java会强制要求程序员进行处理,否则会编译错误,而对于未受检异常则没有这个要求。
三、自定义异常
一般是继承Exception或者它的某个子类。
四、捕获异常
1、try、catch、finally
try代码块:可能抛出异常的代码块
catch代码块:要处理的异常信息,
1)catch可以有多个,每条对应一种异常类型;
2)找到第一个匹配的catch块后,执行catch块内的代码,不在执行其他catch块,如果没有找到,会继续到上层方法中查找。
fianlly:finally 内的代码不管有无异常发生,都会执行。
如果异常没有发生,在try内的代码执行结束后执行;
如果发生异常且被catch捕获,在catch内的代码执行结束后执行
如果有异常发生但没有被捕获,在异常被抛给上层之前执行。
如果try或者catch语句内有return语句,则return语句在finally语句执行结束后执行,但finally并不能改变返回值。
public static int test(){
int ret=10;
try{
return ret;
}finally {
ret=2;
}
}
这里函数的返回值为10,而不是2,。执行过程:在执行到try的return语句时,会将返回值ret保存在一个临时变量中,然后执行finally语句,最后再返回那个临时变量,finally中对ret的修改不会被返回。
如果finally中有return语句,try和catch内的return会丢失,实际返回finally中的返回值。finally有return不仅会覆盖try和catch内的返回值,还会掩盖try和catch内的异常,就当异常没有发生一样。
所以应该避免在finally中使用return语句或者抛出异常。
2、try-with-resources语句
对于文件关闭、资源释放等涉及资源场景,java1.7支持try-with-resources语句,资源的声明和初始化放在try语句内,不用在调用finally,在执行完try语句后,会自动调用资源的close()方法。
对于多个资源,以分号分隔。
try(File file=new File("a.txt")){
//使用资源
}
3、throw与throws
throw:用于声明一个方法可能抛出的异常。
throws:跟在方法的括号后面,可以声明多个异常,以逗号分隔。
对于未受检异常,不要求使用throws进行声明,但对于受检异常,则必须进行声明,换句话说,如果没有声明,则不能抛出。
对于受检异常,不可以抛出而不声明,但可以声明抛出但实际不抛出。主要用于父类方法中声明,父类方法内可能没有抛出,但子类重写方法可能就抛出了,子类不能抛出父类方法中没有声明的受检异常,所以就将所有可能抛出的异常都写在父类上。
一个方法内调用了另一个声明抛出受检异常的方法,则必须处理这个受检异常,可以是catch,也可以继续使用throws。
四、springmvc全局异常处理
全局异常处理,是为了在发生异常时由统一的类进行处理,能够与应用业务代码解耦。
1、声明异常类
需要继承Exception
public class ParamsException extends Exception{
private String code;
private String message;
public ParamsException(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2、异常处理类
@ControllerAdvice
使用@ControllerAdvice声明类,标识该类是一个异常处理类, @ExceptionHandler:作用在方法上,标识要处理的异常类型。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ParamsException.class)
@ResponseBody
public BaseRes<Object> doParamsException(ParamsException exception){
final BaseRes<Object> objectBaseRes = new BaseRes<>();
objectBaseRes.setCode(exception.getCode());
objectBaseRes.setMsg(exception.getMessage());
return objectBaseRes;
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public BaseRes<Object> doMediaTypeException(){
final BaseRes<Object> objectBaseRes = new BaseRes<>();
objectBaseRes.setCode("60000");
objectBaseRes.setMsg("请求方式不正确");
return objectBaseRes;
}
}
3、控制层异常抛出
@RequestMapping(path = {"/test9"}, method = RequestMethod.GET)
@ResponseBody
public String test9(@RequestParam String msg) throws ParamsException {
if ("00".equals(msg)) {
throw new ParamsException("000001", "参数异常处理");
}
//访问外部资源
return "success";
}
实验结果:
4、参数校验异常统一处理
开发中一般碰到参数异常,需要统一处理可以使用一下方法处理:
导入依赖:
<!-- JSR-303 需要的依赖-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
实例类使用@NotNull、@NotBlank、@Length、@Size声明
@NotNull:适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty);
@NotBlank:适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0;
@NotEmpty:适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0;
@Length:适用于String;
@Size:适用于集合、数组、字符序列长度,
实体类
public class User implements Serializable {
@NotNull(message="id不能为空")
private int id;
@NotBlank(message="name不能为空")
private String name;
//@Size(min = 0,max = 2,message="长度应该在0和2位之间")
@Max(value = 99,message="age小于99")
@Min(value = 0,message="age大于0")
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
controller方法
@RequestMapping(path = {"/test8"}, produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
@ResponseBody
public BaseRes test7(@Valid @RequestBody User user, BindingResult bindingResult) throws ParamsException {
logger.info("user:{}", user);
if (bindingResult.hasErrors()) {
final StringBuilder stringBuilder = new StringBuilder();
bindingResult.getAllErrors().forEach((item) -> stringBuilder.append(item.getDefaultMessage()).append(","));
logger.warn("参数异常:{}", bindingResult.getFieldError().getDefaultMessage());
throw new ParamsException("000001", stringBuilder.substring(0,stringBuilder.length()-1).toString());
}
return BaseRes.success();
}
@Valid:这个需要添加到参数上,
BindingResult :用于接收错误信息
结果展示:
5、自定义校验类
自定义一个检验整数大小的类,代替@Min和@Max
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Constraint(validatedBy = MyConstrainValidator.class)
public @interface MyConstraint {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "{com.riant.annotation.message}";
Class<?> [] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class MyConstrainValidator implements ConstraintValidator<MyConstraint, Object> {
private int min;
private int max;
@Override
public void initialize(MyConstraint constraintAnnotation) {
this.max = constraintAnnotation.max();
this.min = constraintAnnotation.min();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (!(value instanceof Integer)) {
return false;
}
int temp = (Integer) value;
if (temp < min || temp > max) {
return false;
}
return true;
}
}
实验结果:
五、@ControllerAdvice 原理
该注解原理是与springmvc的DispatcherServlet有关,
在对DispatcherServlet进行初始化时,会执行initHandlerAdapters(context)和initHandlerExceptionResolvers(context)这两个方法,用于实例处理器适配器和HandlerExceptionResolver,
处理器适配器会实例化RequestMappingHandlerAdapter的bean,该bean和@RequestMapping有关联,
HandlerExceptionResolver用于在处理器映射和执行过程中抛出的异常处理,在controller层抛出异常后,HandlerExceptionResolver会去解决异常,它会根据抛出的一张去找@ExceptionHandler注解的方法,默认会查找当前的controller,如果没有找到会在@ControllerAdvice的切面查找。