springMVC-15 异常处理

异常处理-基本介绍

基本介绍

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

2.主要处理Handler中用@ExceptionHandler注解定义的方法。

3.ExceptionHandlerMethodResolver内部若找不到@ExceptionHandler注解的话,会找@ControllerAdvice类的@ExceptionHandler注解方法,这样就相当于一个全局异常处理器

异常类型

1.局部异常

在注释了@Controller的处理器类Handler,内部写的异常处理,就是局部异常

需要使用注解@在其方法上进行标注

应用实例

处理原因:如果不处理异常,显示界面会非常的不友好

1.创建com/stein/springMVC/exception/MyExceptionHandler.java

注意:

1)Handler要用@Component注入到容器中

2)局部异常处理方法,用使用@ExceptionHandler进行注解

3)@ExceptionHandler的参数,是填写异常类型,并且可以是数组的形式

复制代码
@Controller
public class MyExceptionHandler {

    //一个正常的方法映射
    @RequestMapping("/arithmetic")
    public String exceptionDemo(Integer num){
        Integer result=100/num;
        System.out.println("result= "+result);
        return "success";
    }

    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String exceptionHandler(Exception exception, HttpServletRequest request){
        System.out.println("exception= "+exception.getMessage());
        request.setAttribute("exception", exception.getMessage());
        return "exception_msg";
    }

}

2.创建操作页面 web/exception.jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>exception</title>
</head>
<body>
    <h1>局部异常处理</h1>
    <a href="<%=request.getContextPath()%>/arithmetic?num=0">点击显示算数异常</a>
</body>
</html>

3.创建异常显示页面,web/WEB-INF/page/exception_msg.jsp

复制代码
<body>
    <h1>出错啦>_<</h1>
    <h2>出错信息:<%=request.getAttribute("exception")%></h2>
</body>

4.测试

然后显示出错信息

Debug代码

异常是通过ExceptionHandlerMethodResolver.java类来处理的,它的异常处理机制比较多。通过Ctrl+N找到

通过形参exceptionType拿到异常,然后再拿到处理异常的method,最后通过反射调用。

这一步是通过默认的方法查找异常处理方法,没有找到就返回null

复制代码
Method method = (Method)this.exceptionLookupCache.get(exceptionType);

然后找到了我写的MyExceptionHandler.exceptionHanler(exception,request)方法

不出意外的,找到了我写的异常处理方法

最终返回前端页面进行展示

2.全局异常

应用实例

全局异常处理机制:

如果在ExceptionHandlerMethodResolver 内部找不到**@ExceptionHandler** 注解的话,

会去**@ControllerAdvice** 类找**@ExceptionHandler**注解方法,这样就相当于一个全局异常处理器

1.创建全局异常类,com/stein/springMVC/exception/GlobalException.java

复制代码
//加入这个注解后,就表示是一个全局异常
//全局异常就不管是哪个Handler抛出的异常,都可以捕获
//控制通知
@ControllerAdvice
public class GlobalException {

    //注解参数用于指定,需要捕获的异常类型
    //1. 模拟NumberFormatException
    //2. 在之前的局部异常中,没有对NumberFormatException异常进行涵盖
    //3. 所以就会来找全局异常处理
    @ExceptionHandler({NumberFormatException.class, ClassCastException.class})
    public String global(Exception ex, HttpServletRequest request) {
        System.out.println("全局异常信息:"+ex.getMessage());
        request.setAttribute("exception", ex.getMessage());
        return "exception_msg";
    }
}

2.增加出现转换异常的方法,com/stein/springMVC/exception/MyExceptionHandler.java

复制代码
    @RequestMapping("/global")
    public String globalExceptionDemo(String num){
        int i = Integer.parseInt(num);
        return "success";
    }

3.修改页面,增加装换异常访问请求exception.jsp

复制代码
<body>
    <h1>异常处理</h1>
    <a href="<%=request.getContextPath()%>/arithmetic?num=0">点击显示局部异常</a><br>
    <a href="<%=request.getContextPath()%>/global?num=hello">点击显示全局异常</a>
</body>

4.测试

选择全局异常

显示全局异常提示信息

5.postman测试

Debug处理流程

依然点击全局异常

断点不变,可以看到发生了数字格式化异常,这一步method没有找到处理异常方法,为null

看到此时,method找不到在@Controller类中的局部异常处理方法,显示noMatchingExceptionHandler,没有匹配的异常处理

找到了全局异常处理方法

进入到处理方法

最终成功显示异常信息

注意事项与细节

异常处理时:局部异常优先级高于全局异常

给局部异常处理添加上NumberFormatException.class后,局部异常优先处理

局部异常exception= For input string: "hello"

3.自定义异常(类型)

局部异常和全局异常,是通过作用范围来区分的。

自定义异常,说的是自定义异常的类型。比如有ArithmeticException.class, NullPointerException.class,NumberFormatException.class这些异常了,我还需要自定义一个年龄异常AgeException.class

通过@ResponseStatus注解,可以自定义该异常

关系:那么自定义异常(类型),可以通过默认tomcat调用,或者局部异常、全局异常进行选择调用。像这样:

复制代码
@ExceptionHandler({ArithmeticException.class, NullPointerException.class,NumberFormatException.class,ArithmeticException.class})
应用实例

1.创建自定义异常类型,com/stein/springMVC/exception/AgeException.java

复制代码
//reason表示显示的错误原因
//value填写Http错误状态,是枚举类型的
@ResponseStatus(value = HttpStatus.BAD_REQUEST,reason = "年龄需要在1-120岁之间")
public class AgeException extends RuntimeException {
}

2.添加可能出现自定义异常的方法,MyExceptionHandler.java

复制代码
    @RequestMapping("/age")
    public String age(int age){
        if(age>0 && age<120){
            return "success";
        }else {
            throw new AgeException();
        }
    }

3.添加访问链接,exception.jsp

复制代码
<a href="<%=request.getContextPath()%>/age?age=121">点击显示自定义异常</a><br><br>

4.测试

自定义异常(类型)

返回结果


这个tomcat默认页面太生硬,想用自己页面显示

1.把AgeException.class添加到GlobalException.global()的注解中

复制代码
@ExceptionHandler({NumberFormatException.class, ClassCastException.class,AgeException.class})

2.为了配合exception_msg.jsp的属性exception.getMessage()返回结果不为null,添加AgeException.java的构造器

复制代码
@ResponseStatus(value = HttpStatus.BAD_REQUEST,reason = "年龄需要在1-120岁之间")
public class AgeException extends RuntimeException {
    public AgeException() {
    }

    public AgeException(String message) {
        super(message);
    }
}

3.业务代码中,完善message

复制代码
    @RequestMapping("/age")
    public String age(int age){
        if(age>0 && age<120){
            return "success";
        }else {
            throw new AgeException("年龄需要在1-120岁之间");
        }
    }

4.再次测试

点击自定义异常后,出现了我们自己写的异常页面exception_msg.jsp

同样,这个自定义异常类型,添加到局部异常也可以生效,这儿就不演示了。

Debug处理流程

跟全局异常Debug一样,参考走一遍即可。

统一处理异常信息

SimpleMappingExceptionResolver

  • 基本说明

1.如果希望对所有异常进行统一处理,可以使用SimpleMappingExceptionResolver

2.它将异常类名映射为视图名,即发生异常时使用对应的视图 报告异常

3.需要在ioc容器中配置

应用实例

1.配置SimpleMappingExceptionResolver

复制代码
    <!--配置一个统一异常处理-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--这个属性用于设置异常映射,即当发生某种异常时,应该跳转到哪个视图。-->
        <property name="exceptionMappings">
            <!--它包含一个`<props>`标签,这是Spring配置中用于定义`java.util.Properties`类型属性的方式。每个`<prop>`标签表示一个键值对。-->
            <props>
                <!--键(key)是异常类的全限定名-->
                <!--值(value)是`arrEx`,是一个逻辑视图页面-->
                <prop key="ArrayIndexOutOfBoundsException">arrEx</prop>
            </props>
        </property>
    </bean>

2.按配置创建异常视图,web/WEB-INF/page/arrEx.jsp

复制代码
<h1>朋友,当前页面异常>_<,如有疑问,可以联系管理员</h1>

3.增加出现数组越界的映射方法,MyExceptionHandler

复制代码
@RequestMapping("/array")
public String array(){
    //两种写法都可以
    int[] ints = {1, 3, 6, 12};
    //int[] ints =new int[]{1,3,6,12};
    //越界异常
    System.out.println("ints[5]="+ints[5]);
    return "success";
}

4.添加访问页面的链接,exception.jsp

复制代码
    <a href="<%=request.getContextPath()%>/array">点击测试统一异常处理</a><br><br>

5.测试

对未知异常统一处理

异常的情况有很多,当无法对每一种异常都进行单独配置的时候,需要一种兜底的方案,就是对其他异常的统一处理。

应用实例

1.使用SimpleMappingExceptionResolver进行配置,web/WEB-INF/springMVC-servlet.xml

复制代码
    <!--配置一个统一异常处理-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--这个属性用于设置异常映射,即当发生某种异常时,应该跳转到哪个视图。-->
        <property name="exceptionMappings">
            <!--它包含一个`<props>`标签,这是Spring配置中用于定义`java.util.Properties`类型属性的方式。每个`<prop>`标签表示一个键值对。-->
            <props>
                <!--键(key)是异常类的全限定名-->
                <!--值(value)是`arrEx`,是一个逻辑视图页面-->
                <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
                <!--两种写法都可以,但是推荐上面一种,避免重名-->
                <!--<prop key="ArrayIndexOutOfBoundsException">arrEx</prop>-->
                <!--配置其他的未知异常-->
                <!--key配置的是所有异常Exception-->
                <!--响应的页面名称是allEx-->
                <prop key="java.lang.Exception">allEx</prop>
            </props>
        </property>
    </bean>

2.创建响应页面,web/WEB-INF/page/allEx.jsp

复制代码
    <h1>哎呀,发生了未知异常>_<</h1>

3.添加未知异常映射方法,MyExceptionHandler.java

这是一个字符串越界异常

复制代码
    @RequestMapping("/unknown")
    public String unknownException(){
        String str="hello";
        System.out.println("str.charAt[5]="+str.charAt(5));
        return "success";
    }

4.添加访问链接,exception.jsp

复制代码
    <a href="<%=request.getContextPath()%>/unknown">配置统一的未知异常处理</a><br><br>

5.测试

哎呀,发生了未知异常>_<

异常处理的优先级

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

1.对比测试局部异常、全局异常、SimpleMappingExceptionResolver的优先级,他们3个的范围都加上ArrayIndexOutOfBoundsException.class,然后进行数组越界异常测试。

结果响应的只有局部异常:

局部异常exception= 5

2.接下来取消局部异常的参赛资格,取消其ArrayIndexOutOfBoundsException.class的设置,将剩下2个的进行对比。然后进行数组越界异常测试。

此时,响应的是全局异常

全局异常信息:5

3.然后全局异常退出,删除其越界异常的设置,只保留SimpleMappingExceptionResolver的设置,进行越界测试。

结果是按照SimpleMappingExceptionResolver配置的arrEx.jsp进行了响应

朋友,当前页面异常>_<,如有疑问,可以联系管理员

4.将SimpleMappingExceptionResolver的设置也取消,包括越界异常和所有异常。进行越界测试

结果,Tomcat默认的异常机制开始生效了

综上,确认了他们的优先级排序。

相关推荐
牛马baby2 分钟前
synchronized 做了哪些优化?
java·高并发·并发编程·synchronized·锁升级·面试资料·程序员涨薪跳槽
钟琛......7 分钟前
Java事务失效(面试题)的常见场景
java·数据库·spring
带刺的坐椅1 小时前
Solon Expression Language (SnEL):轻量高效的Java表达式引擎
java·solon·snel·表达式语言
老马啸西风1 小时前
从零开始手写redis(18)缓存淘汰算法 FIFO 优化
java
Java中文社群1 小时前
超实用!SpringAI提示词的4种神级用法
java·人工智能·后端
代码or搬砖2 小时前
Spring JDBC配置与讲解
java·数据库·spring
魔芋红茶2 小时前
Spring 源码学习 3:工厂后处理器
java·学习·spring
m0_634865402 小时前
sa-token:我将代替你,Spring Security
java·后端·spring
蚰蜒螟2 小时前
Spring与SLF4J/Logback日志框架深度解析:从源码看日志系统设计
java·spring·logback
IT_10242 小时前
springboot企业级项目开发之项目测试——集成测试!
spring boot·后端·spring·集成测试