异常处理

1.基本介绍

  1. SpringMVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler映射、数据绑定以及目标方法执行时发生的异常

  2. 有两种方案来进行异常处理:

    a.在本类编写处理异常的方法,将抛出的异常视为局部异常处理

    b.额外编写处理异常的类,将抛出的异常视为全局异常处理

  3. 主要处理的是 Handler 中使用了 @ExceptionHandler 注解修饰的方法(局部异常处理)

  4. ExceptionHandlerMethodResolver 内部若找不到上述 @ExceptionHandler 注解修饰的方法,就会去找有 @ControllerAdvice 注解修饰的类中含有 @ExceptionHandler 注解的方法,被 @ControllerAdvice 修饰的类称为全局处理器(全局异常处理)

  5. 异常处理时,局部异常优先级高于全局异常

2.局部异常

2.1应用实例

(1)创建 MyExceptionHandler.java,在这个 Handler 中模拟各种出现的异常,并在本类添加处理可能出现的异常的方法(局部异常)

局部异常的处理方法只能处理本类中出现的异常。

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; /**
* @author 李
* @version 1.0
*/
@Controller
public class MyExceptionHandler {
/**
* 1.localException()方法处理局部异常
* 2.指定处理 算术异常、空指针异常
* 3. Exception ex 本类生成的异常对象,会传递给ex,通过ex可以拿到相关信息,
* 在这里可以加入业务逻辑
* @param ex
* @param request
* @return
*/
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String localException(Exception ex, HttpServletRequest request) {
System.out.println("局部异常信息是=" + ex.getMessage());
//如何将异常的信息带到下一个页面(根据你的业务逻辑)
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
} /**
* 1.编写方法,模拟异常-算术异常
* 2.如果我们不做异常处理,是由tomcat默认页面处理
* @param num
* @return
*/
@RequestMapping(value = "/testException01")
public String test01(Integer num) {
int i = 9 / num;
return "success";
}
}

(2)exception_mes.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常信息</title>
</head>
<body>
<h1>异常信息提示</h1>
<h4>${requestScope.reason}</h4>
</body>
</html>

(3)测试,直接访问 http://localhost:8080/springmvc/testException01?num=0,显示结果如下:

捕获到了异常,SpringMVC 底层反射调用了 localException 方法进行异常处理,跳转到了 exception_mes.jsp 页面提示异常信息。

如果我们没有处理异常,默认是由 tomcat 进行异常处理

2.2执行流程

从发生异常到捕获异常并调用处理方法,局部异常处理的整个执行流程是怎样的呢?

第一步:浏览器访问目标方法,如果出现异常,会到 ExceptionHandlerMethodResolver 类中执行 resolveMethodByExceptionType() 方法

第二步:执行resolveMethodByExceptionType() 方法,得到处理异常的方法。

@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
//获取目标方法出现的异常类型 exceptionType
//然后通过map去找有没有一个默认的方法去处理这个异常
Method method = this.exceptionLookupCache.get(exceptionType);
if (method == null) {//如果没有默认方法
//getMappedMethod 得到该异常映射的处理方法,如例子中的 localException() 方法
method = getMappedMethod(exceptionType);
//将找到的这个方法作为 该异常的处理方法,放入到map中
this.exceptionLookupCache.put(exceptionType, method);
}
//返回这个方法
return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
}

第三步:将得到的异常数据放入到该方法的参数中,底层反射调用处理异常的该方法

第四步:根据处理异常方法,执行自定义的业务逻辑。如例子中跳到某个页面并提示异常信息。

3.全局异常

3.1应用实例

演示全局异常处理机制。

ExceptionHandlerMethodResolver 内部若找不到 @ExceptionHandler 注解的方法,就会找 @ControllerAdvice 类的 @ExceptionHandler 注解方法,这样就相当于一个全局处理器,全局处理器可以处理不同的 Handler 出现的异常。

(1)自定义一个全局处理器 MyGlobalException.java

package com.li.web.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpServletRequest; /**
* @author 李
* @version 1.0
* 如果一个类上标注了 @ControllerAdvice,那么这个类就是一个全局异常处理器
*/
@ControllerAdvice
public class MyGlobalException {
/**
* 1.不管是哪个 Handler抛出的异常,全局异常都可以捕获
* 2.@ExceptionHandler({这里是可以捕获的异常类型})
* @param ex 接收抛出的异常对象
* @return
*/
@ExceptionHandler({NumberFormatException.class, ClassCastException.class})
public String globalException(Exception ex, HttpServletRequest request) {
System.out.println("全局异常处理=" + ex.getMessage());
//业务处理-将异常信息带到下一个页面并显示
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
}

(2)在 Handler 的目标方法中模拟异常

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; /**
* @author 李
* @version 1.0
*/
@Controller
public class MyExceptionHandler {
//局部异常处理方法
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String localException(Exception ex, HttpServletRequest request) {
System.out.println("局部异常信息是=" + ex.getMessage());
//如何将异常的信息带到下一个页面(根据你的业务逻辑)
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
} //目标方法
@RequestMapping(value = "testGlobalException")
public String global() {
//模拟一个 NumberFormatException异常,若该异常不能在局部异常方法处理,
//就会被交到全局异常处理器中处理
int num = Integer.parseInt("hello");
return "success";
}
}

(3)exception_mes.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常信息</title>
</head>
<body>
<h1>异常信息提示</h1>
<h4>${requestScope.reason}</h4>
</body>
</html>

(3)在浏览器中访问目标方法 http://localhost:8080/springmvc/testGlobalException,可以看到页面提示了异常信息,同时后台输出为 全局异常处理=For input string: "hello",这说明是全局异常处理器捕获到的异常,局部异常方法没有捕获到。

3.2执行流程

从发生异常到捕获异常并调用处理方法,全局异常处理的流程如下:

(1)浏览器访问目标方法,若出现异常,会到 ExceptionHandlerExceptionResolver 类的 ServletInvocableHandlerMethod() 方法进行处理:

(2)上述方法首先会到 ExceptionHandlerMethodResolver 类中执行 resolveMethod 方法

(3)resolveMethod 方法又调用本类的 resolveMethodByThrowable 方法

(4)resolveMethodByThrowable 继续调用本类的 resolveMethodByExceptionType() 方法:

@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
//获取目标方法的异常类型 exceptionType
//通过map去找处理该异常的默认的方法
Method method = this.exceptionLookupCache.get(exceptionType);
if (method == null) {//若没有默认方法
//得到该异常映射的处理方法,这一步如果没有在Handler类中找到处理方法,
//如局部异常方法不匹配该异常,就会返回一个 noMatchingExceptionHandler() 的方法
method = getMappedMethod(exceptionType);
//将找到的这个方法作为 该异常的处理方法,放入到map中
this.exceptionLookupCache.put(exceptionType, method);
}
//若 method为 noMatchingExceptionHandler(),则返回一个 null
return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
}

(5)如果 resolveMethodByExceptionType() 方法没有在本类 Handler 中匹配到可以解决出现的异常的方法,就返回 null

@Nullable
public Method resolveMethodByThrowable(Throwable exception) {
//如果 mothod 返回 null
Method method = resolveMethodByExceptionType(exception.getClass());
if (method == null) {
//获取出现异常的原因
Throwable cause = exception.getCause();
if (cause != null) {
//根据异常找匹配的方法
method = resolveMethodByThrowable(cause);
}
}
//如果返回null
return method;
}

(6)一路返回到 ExceptionHandlerExceptionResolver 类的 ServletInvocableHandlerMethod() 方法,如果获取到的 method 仍为 null,就会去遍历带有 @ControllerAdvice 注解修饰的类,然后在该类(全局处理器)中获取可以处理异常的方法。

这里的 entry.getKey() 就是 @ControllerAdvice 注解修饰的类对象。

(7)最后反射调用找到的方法。

4.自定义异常

可以通过 @ResponseStatus 注解,来自定义异常的说明

4.1应用实例

(1)自定义异常

package com.li.web.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; /**
* @author 李
* @version 1.0
*/
@ResponseStatus(reason = "年龄需要在1-120之间",value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException{
}

(2)修改 MyExceptionHandler.java,增加方法进行测试

package com.li.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; /**
* @author 李
* @version 1.0
*/
@Controller
public class MyExceptionHandler {
@RequestMapping(value = "/testException02")
public String test02() {
throw new AgeException();
}
}

(3)浏览器访问 http://localhost:8080/springmvc/testException02,测试显示如下:

访问目标方法,抛出异常,异常的信息就是你指定的信息,状态码也是你指定的数据

4.2拓展

自定义异常可以被局部异常处理和全局异常处理接管,只需要在 @ExceptionHandler 注解中添加自定义异常的.class即可

当然,如果想要拿到 ex.getMessage() 信息,需要在自定义的异常类创建带有 message 参数的构造器

自定义异常的 reason,value 属性是给 tomcat 的默认页面显示的,而构造器的 messgae 属性是传入对象的

然后在创建异常类对象的时候放入提示信息。


自定义异常本质就是异常,它的处理流程由 它被局部异常还是全局异常接管 而定。如果两者都没有接管,就会被 tomcat 来处理,然后 tomcat 显示一个默认的页面。

5.SimpleMappingExceptionResolver

5.1基本说明

  1. 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver
  2. 它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
  3. 需要在 spring 的容器文件中配置

5.2应用实例

需求:使用 SimpleMappingExceptionResolver 对数据越界异常进行统一处理

(1)修改 MyExceptionHandler.java,增加方法 test03

//模拟数据下标越界异常
@RequestMapping(value = "/testException03")
public String test03() {
int[] arr = {3, 8, 18, 20};
System.out.println(arr[99]);
return "success";
}

(2)在 spring 容器文件配置

<!--配置统一异常处理的bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--key为处理的异常的全路径,
arrEx为出现异常后要跳转的页面(页面所在的目录要和你的视图解析器的前后缀匹配)-->
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
</props>
</property>
</bean>

(3)arrEx.jsp,该页面显示异常信息

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>数组下标越界异常</title>
</head>
<body>
异常信息:数组下标越界异常
</body>
</html>

(4)浏览器访问目标方法,返回的页面显示:跳转到了 arrEx.jsp 页面。

目标方法发生异常--->先去局部异常方法处理--->如果不行,到全局异常处理器处理--->如果不行,到容器文件配置统一异常处理的 bean 处理--->都不行,最后由 tomcat 处理

5.3对未知异常进行统一处理

在实际开发中,异常的种类非常多,我们的异常处理方法可能不能捕获到所有的异常。

如何处理没有归类(未知的)的异常?

仍然可以使用 SimpleMappingExceptionResolver 进行处理。只需要在配置的时候,将捕获的异常范围扩大,如Exception。

例子

(1)修改 MyExceptionHandler.java,增加方法 test04

//如果发生了没有归类的异常,可以给出统一的提示页面
@RequestMapping(value = "/testException04")
public String test04() {
String str = "hello";
//StringIndexOutOfBoundsException
char c = str.charAt(10);
return "success";
}

(2)容器文件配置处理异常的bean时,扩大捕获范围

<!--配置统一异常处理的bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
<!--捕获未知异常-->
<prop key="java.lang.Exception">allEx</prop>
</props>
</property>
</bean>

(3)allEx.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常通知</title>
</head>
<body>
<h1>系统发生了未知异常..</h1>
</body>
</html>

(4)浏览器访问目标方法,访问结果如下:

5.4异常处理的优先级

局部异常处理 > 全局异常处理 > SimpleMappingExceptionResolver 处理 > tomcat 默认机制处理

day14-异常处理的更多相关文章

  1. Python基础篇【第3篇】: Python异常处理、反射、动态导入、利用反射的web框架

    异常处理 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常.异常是Python对象,表示一个错误.当P ...

  2. 【JAVA零基础入门系列】Day14 Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  3. 关于.NET异常处理的思考

    年关将至,对于大部分程序员来说,马上就可以闲下来一段时间了,然而在这个闲暇的时间里,唯有争论哪门语言更好可以消磨时光,估计最近会有很多关于java与.net的博文出现,我表示要作为一个吃瓜群众,静静的 ...

  4. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  5. 异常处理汇总 ~ 修正果带着你的Net飞奔吧!

    经验库开源地址:https://github.com/dunitian/LoTDotNet 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983 ...

  6. JavaScript var关键字、变量的状态、异常处理、命名规范等介绍

    本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...

  7. IL异常处理

    异常处理在程序中也算是比较重要的一部分了,IL异常处理在C#里面实现会用到一些新的方法 1.BeginExceptionBlock:异常块代码开始,相当于try,但是感觉又不太像 2.EndExcep ...

  8. Spring MVC重定向和转发以及异常处理

    SpringMVC核心技术---转发和重定向 当处理器对请求处理完毕后,向其他资源进行跳转时,有两种跳转方式:请求转发与重定向.而根据要跳转的资源类型,又可分为两类:跳转到页面与跳转到其他处理器.对于 ...

  9. 【repost】JS中的异常处理方法分享

    我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...

  10. 札记:Java异常处理

    异常概述 程序在运行中总会面临一些"意外"情况,良好的代码需要对它们进行预防和处理.大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如非法输入,不正当的操作 ...

随机推荐

  1. layui的图片上传使用

    先上效果图. 在用之前呢,你得先更新最新版的layui版本.经验之谈_(:_」∠)_ 今天在用的时候,实在是碰到太多的坑,本来是拒绝更新到最新版来着. 首先是layui.js和layui.all.js ...

  2. jquery组件解决option选项框的样式自定义方案

    记录一下今天工作中遇到的一个需求和自行找到的解决办法 需求: 在原始的select选项框中的增加一个标识.(我想增加一个具有样式的span元素,试了半天在option里无法添加span,更别说具有样式 ...

  3. SpringCLoud_Aibaba

    微服务项目核心组件 https://gitee.com/gtnotgod/spring-cloud_-alibaba_-study001.git 注册中心:nacos API网关:gateway 生产 ...

  4. 【Java SE】Day02 数据类型转换、运算符、方法入门

    一.数据类型转换 1.自动转换 取值范围小在运算时会提升为取值范围大的类型 byte+int=int int+double=double 转换规则:byte.short.char-->int-- ...

  5. USB限流IC,限流开关保护芯片

    PW1503和PW1502是超低RDS(ON)开关,具有可编程电流限制的USB限流IC,以保护电源于过电流负载和正极负极短路的保护.它具有过温保护以及反向闭锁功能. PW1503,PW1502均采用S ...

  6. 为什么推荐Kestrel作为网络开发框架

    为什么推荐Kestrel 网络框架千千万万,在dotnet平台,我们可以直接手撸Socket,也可以基于dotnetty来开发,或者选择某些第三方类似于dotnetty的网络库,为何我要推荐Kestr ...

  7. 如何通过C#合并Word文档?

    合并Word文档可以快速地将多份编辑好的文档合在一起,避免复制粘贴时遗漏内容,以及耗费不必要的时间,同时,也方便了人们阅读或者对其进行再次修改.例如,在我们进行团队作业的时候,每个人都会有不同的分工, ...

  8. 解决 ERROR: Could not find a version that satisfies the requirement xxx 的问题

    解决 ERROR: Could not find a version that satisfies the requirement xxx 的问题 1.解决 ERROR: Could not find ...

  9. STM32与PS2的无线通信和相关函数介绍

    PS2采用SPI通信协议 源码和参考文件获取:https://github.com/Sound-Sleep/PS2_Based_On_STM32 接收器接口 DI:手柄->主机,时钟的下降沿传送 ...

  10. 注意看,她叫小美,在地址栏输入URL地址后发生了什么?

    注意看,这个用户叫小美,他在地址栏输入了一串URL地址,然后竟然发生了不可思议的事情! 01.输入URL发生了什么? 从输入URL开始,到页面呈现出来,简单来说分为四个步骤: ① 建立连接:建立与服务 ...