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默认的异常机制开始生效了

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

相关推荐
winrisef1 天前
删除无限递归文件夹
java·ide·python·pycharm·系统安全
悦悦子a啊1 天前
Java面向对象练习:Person类继承与排序
java·开发语言·python
不会算法的小灰1 天前
Spring Boot 实现邮件发送功能:整合 JavaMailSender 与 FreeMarker 模板
java·spring boot·后端
come112341 天前
深入理解 Java和Go语法和使用场景(指南十一)
java·开发语言·golang
李贺梖梖1 天前
DAY23 单例设计模式、多例设计模式、枚举、工厂设计模式、动态代理
java
武昌库里写JAVA1 天前
Java设计模式之工厂模式
java·vue.js·spring boot·后端·sql
赛姐在努力.1 天前
SpringMVC中的常用注解及使用方法
java·spring
让我上个超影吧1 天前
黑马点评秒杀优化和场景补充
java
寻星探路1 天前
Java EE初阶启程记06---synchronized关键字
java·java-ee
沉木渡香1 天前
【VSCode中Java开发环境配置的三个层级之Maven篇】(Windows版)
java·vscode·maven