day14-异常处理
异常处理
1.基本介绍
SpringMVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler映射、数据绑定以及目标方法执行时发生的异常
有两种方案来进行异常处理:
a.在本类编写处理异常的方法,将抛出的异常视为局部异常处理
b.额外编写处理异常的类,将抛出的异常视为全局异常处理
主要处理的是 Handler 中使用了 @ExceptionHandler 注解修饰的方法(局部异常处理)
ExceptionHandlerMethodResolver 内部若找不到上述 @ExceptionHandler 注解修饰的方法,就会去找有 @ControllerAdvice 注解修饰的类中含有 @ExceptionHandler 注解的方法,被 @ControllerAdvice 修饰的类称为全局处理器(全局异常处理)
异常处理时,局部异常优先级高于全局异常
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基本说明
- 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver
- 它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
- 需要在 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-异常处理的更多相关文章
- Python基础篇【第3篇】: Python异常处理、反射、动态导入、利用反射的web框架
异常处理 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常.异常是Python对象,表示一个错误.当P ...
- 【JAVA零基础入门系列】Day14 Java对象的克隆
今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...
- 关于.NET异常处理的思考
年关将至,对于大部分程序员来说,马上就可以闲下来一段时间了,然而在这个闲暇的时间里,唯有争论哪门语言更好可以消磨时光,估计最近会有很多关于java与.net的博文出现,我表示要作为一个吃瓜群众,静静的 ...
- 基于spring注解AOP的异常处理
一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...
- 异常处理汇总 ~ 修正果带着你的Net飞奔吧!
经验库开源地址:https://github.com/dunitian/LoTDotNet 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983 ...
- JavaScript var关键字、变量的状态、异常处理、命名规范等介绍
本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...
- IL异常处理
异常处理在程序中也算是比较重要的一部分了,IL异常处理在C#里面实现会用到一些新的方法 1.BeginExceptionBlock:异常块代码开始,相当于try,但是感觉又不太像 2.EndExcep ...
- Spring MVC重定向和转发以及异常处理
SpringMVC核心技术---转发和重定向 当处理器对请求处理完毕后,向其他资源进行跳转时,有两种跳转方式:请求转发与重定向.而根据要跳转的资源类型,又可分为两类:跳转到页面与跳转到其他处理器.对于 ...
- 【repost】JS中的异常处理方法分享
我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...
- 札记:Java异常处理
异常概述 程序在运行中总会面临一些"意外"情况,良好的代码需要对它们进行预防和处理.大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如非法输入,不正当的操作 ...
随机推荐
- 动态规划篇——DP问题
动态规划篇--DP问题 本次我们介绍动态规划篇的DP问题,我们会从下面几个角度来介绍: 区间DP 计数DP 树状DP 记忆化搜索 区间DP 我们通过一个案例来讲解区间DP: /*题目展示*/ 题目名: ...
- MAUI Blazor (Windows) App 动态设置窗口标题
接着上一篇"如何为面向 Windows 的 MAUI Blazor 应用程序设置窗口标题?" Tips: 总所周知,MAUI 除了 Windows App 其他平台窗口是没有 Ti ...
- 【Java EE】Day01 基础加强、Junit单元测试、反射、注解
〇.总结 1.测试:三个注解.断言判断 2.反射:三个阶段获取字节码对象的三种方式.忽略成员变量权限方法setAccessible(true) 3.注解:内置注解SupressWarning& ...
- Redis Zset实现统计模块
1. 背景 公司有一个配置中心系统,使用MySQL存储了大量的配置,但现在不清楚哪些配置正在线上使用,哪些已经废弃了,所以需要实现一个统计模块,实现以下两个功能: 查看总体配置的数量以及活跃的数量 查 ...
- Jmeter 之 jp@gc - Stepping Thread Group
jp@gc - Stepping Thread Group 自定义线程组,根据业务需要进行设计用户增加间隔时间等 1. 下载jmeter-plugins-manager-1.3.jar插件放入lib ...
- 【机器学习】李宏毅——Unsupervised Learning
读这篇文章之间欢迎各位先阅读我之前写过的线性降维的文章.这篇文章应该也是属于Unsupervised Learning的内容的. Neighbor Embedding Manifold Learnin ...
- 第二章 --------------------XAML基础
1.XAML是什么? XAML是扩展标记语言,是为了方便设计人员设计UI界面.具体关于XAML语法的讲解参考其他相关书籍. XAML每一个标签以<>开头,以</>结尾,作为标 ...
- 第六节 FAF与GP不工作保护区的绘制
飞行程序设计软件实践 前一篇文章中,通过风标设计2023插件,我们在CAD中绘制了FAP方式下的精密进近保护区. 接着这个话题我们继续来看一下FAF方式下的保护区应该怎样绘制,以及OAS参数的其它用法 ...
- [cocos2d-x]关于动画
声明一下:看见这篇文章总结的已经非常好了,没必要再去自己到处东翻西找了,链接:http://shahdza.blog.51cto.com/2410787/1546998 [唠叨] 基本动画制作需要用到 ...
- 03-Sed基础语法及例子
1 Sed语法及举例 在实际使用sed过程中经常使用字符串的替换.删除.查找等操作.Linux中的编辑器Vi.GVIM.emacs等都可以进行上述操作,但是大量进行操作的时候,效率很低. 地址参数 { ...