• SpringMVC异常处理


    SpringMVC异常处理

    1.异常处理-基本介绍

    ● 基本介绍

    1. Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据 绑定以及目标方法执行时发生的异常
    2. 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法
    3. ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器

    2.局部异常

    1.应用实例

    ● 应用实例需求

    • 如果不处理异常, 非常的不友好

    ● 应用实例-代码实现

    exception_mes.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>异常信息提示</title>
    </head>
    <body>
    <h1>朋友, 程序发生了异常...</h1>
    异常信息- ${requestScope.reason}
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    success.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>操作成功</title>
    </head>
    <body>
    <h1>恭喜, 操作成功~</h1>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    MyExceptionHandler.java

    @Controller
    public class MyExceptionHandler {
    
    
        @ExceptionHandler(value={ArithmeticException.class,NullPointerException.class})
        public String handlerException(Throwable e, HttpServletRequest request){
            System.out.println("异常信息:"+e.getMessage());
            request.setAttribute("reason",e.getMessage());
            return "exception_mes";
        }
    
        /**
         * 1. 编写方法,模拟异常, 算术异常
         * 2. 如果我们不做异常处理,是由tomcat默认页面显示
         *
         * @param num
         * @return
         */
        @RequestMapping(value = "/testException01")
        public String test01(Integer num) {
            int i = 9 / num;
            return "success";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    测试

    image-20220625092207672

    2.Debug处理流程

    image-20220625121854555

    /**
     	找到一个Method来处理给定的 Throwable。
    	如果找到多个匹配项,则使用ExceptionDepthComparator 
     */
    @Nullable
    public Method resolveMethodByThrowable(Throwable exception) {
        //根据异常类型获取处理异常的目标方法
       Method method = resolveMethodByExceptionType(exception.getClass());
       if (method == null) {
          Throwable cause = exception.getCause();
          if (cause != null) {
             method = resolveMethodByThrowable(cause);
          }
       }
       return method;
    }
    
    @Nullable
    public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
        //根据异常类型从缓存中获取目标方法
       Method method = this.exceptionLookupCache.get(exceptionType);
       if (method == null) {
           //controller含有@ExceptionHandler则可以获取到目标方法
          method = getMappedMethod(exceptionType);
          this.exceptionLookupCache.put(exceptionType, method);
       }
       return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    image-20220625123953038

    image-20220625124029924

    image-20220625124052522

    image-20220625124118652

    3.全局异常

    1.应用实例

    ● 应用实例需求

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

    ● 应用实例-代码实现

    1.MyExceptionHandler.java新增异常,这里我们故意定义一个本类异常处理不包含的异常

    @Controller
    public class MyExceptionHandler {
    
    
        @ExceptionHandler(value={ArithmeticException.class,NullPointerException.class})
        public String handlerException(Throwable e, HttpServletRequest request){
            System.out.println("异常信息:"+e.getMessage());
            request.setAttribute("reason",e.getMessage());
            return "exception_mes";
        }
    
        /**
         * 1. 编写方法,模拟异常, 算术异常
         * 2. 如果我们不做异常处理,是由tomcat默认页面显示
         */
        @RequestMapping(value = "/testException01")
        public String test01(Integer num) {
            int i = 9 / num;
            return "success";
        }
    
        /**
         * 模拟 NumberFormatException异常
         * @return
         */
        @RequestMapping(value = "/testException02")
        public String test02() {
            int num = Integer.parseInt("hello");
            return "success";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2.全局异常处理类-MyGlobalException.java

    @ControllerAdvice
    public class MyGlobalException {
    
        @ExceptionHandler(value = {ClassCastException.class,NumberFormatException.class})
        public String exceptionHandler(Throwable e, HttpServletRequest request){
            System.out.println("全局异常处理-" + e.getMessage());
            //如何将异常的信息带到下一个页面.
            request.setAttribute("reason", e.getMessage());
            return "exception_mes";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试

    image-20220625124502655

    2.Debug处理流程

    可以看到SpringMVC底层在获取目标异常处理方法时,会先找本类的@ExceptionHandler修饰的异常处理方法

    如果没有找到则会从被@ControllerAdvice修饰的全局异常处理类中查找,如果找到了则执行异常处理的目标方法

    @Nullable
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
          @Nullable HandlerMethod handlerMethod, Exception exception) {
    
       Class<?> handlerType = null;
    
       if (handlerMethod != null) {
          // Local exception handler methods on the controller class itself.
          // To be invoked through the proxy, even in case of an interface-based proxy.
          handlerType = handlerMethod.getBeanType();
          ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
          if (resolver == null) {
             resolver = new ExceptionHandlerMethodResolver(handlerType);
             this.exceptionHandlerCache.put(handlerType, resolver);
          }
          Method method = resolver.resolveMethod(exception);
          if (method != null) {
             return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
          }
          // For advice applicability check below (involving base packages, assignable types
          // and annotation presence), use target class instead of interface-based proxy.
          if (Proxy.isProxyClass(handlerType)) {
             handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
          }
       }
    	
        //从被@ControllerAdvice修饰的类中查找异常
       for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
          ControllerAdviceBean advice = entry.getKey();
          if (advice.isApplicableToBeanType(handlerType)) {
             ExceptionHandlerMethodResolver resolver = entry.getValue();
                        //public java.lang.String com.llp.web.exception.MyGlobalException.exceptionHandler(java.lang.Throwable,javax.servlet.http.HttpServletRequest)
             Method method = resolver.resolveMethod(exception);
             if (method != null) {
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
             }
          }
       }
    
       return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    image-20220625124848376

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

    我们在全局异常类中添加上空指针的异常处理

    @ExceptionHandler(value = {ClassCastException.class,NumberFormatException.class,NullPointerException.class})
    public String exceptionHandler(Throwable e, HttpServletRequest request){
        System.out.println("全局异常处理-" + e.getMessage());
        //如何将异常的信息带到下一个页面.
        request.setAttribute("reason", e.getMessage());
        return "exception_mes";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    再次进行测试

    可以看到底层执行顺序是先查找局部的异常处理

    image-20220625125553245

    image-20220625125822956

    4.自定义异常

    1.应用实例

    1.BaseException-自定义异常处理类

    /**
     * 自定义异常类
     */
    @ResponseStatus(value = HttpStatus.BAD_REQUEST,code =HttpStatus.BAD_REQUEST,reason = "错误原因")
    public class BaseException extends RuntimeException{
        public BaseException() {
        }
    
        public BaseException(String message) {
            super(message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.新增测试方法

    @RequestMapping(value = "/testException03")
    public String test03() {
        throw new BaseException("自定义异常描述");
    }
    
    • 1
    • 2
    • 3
    • 4

    3.测试

    这里我们没有对自定义异常进行局部/全局处理,最终异常由tomcat默认机制进行处理

    image-20220625130155154

    在全局异常处理我们自定义的异常再次测试

    @ControllerAdvice
    public class MyGlobalException {
    
        @ExceptionHandler(value = {ClassCastException.class,NumberFormatException.class,NullPointerException.class,BaseException.class})
        public String exceptionHandler(Throwable e, HttpServletRequest request){
            System.out.println("全局异常处理-" + e.getMessage());
            //如何将异常的信息带到下一个页面.
            request.setAttribute("reason", e.getMessage());
            return "exception_mes";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20220625130618220

    5.SimpleMappingExceptionResolver

    1.基本说明

    ● 基本说明

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

    2.应用实例

    ● 应用实例-需求

    对数组越界异常进行统一处理,使用 SimpleMappingExceptionResolver

    ● 应用实例-代码实现

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

    @RequestMapping(value = "/testException04")
    public String test04() {
        int[] arr = new int[]{3, 9, 10, 190};
        //抛出一个数组越界的异常 ArrayIndexOutOfBoundsException
        System.out.println(arr[90]);
        return "success";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 配置 springDispatcherServlet-servlet.xml

    <!--配置一个统一异常处理-->
    <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--key:异常类型,arrEx对应的视图-->
                <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
            </props>
        </property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.arrEx.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>数组越界异常</title>
    </head>
    <body>
    <h1>异常信息: 数组越界异常</h1>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试

    image-20220625131456941

    3.对未知异常进行统一处理

    ● 应用实例-需求

    对未知异常进行统一处理,使用 SimpleMappingExceptionResolver

    1. 修改 MyExceptionHandler.java , 增加方法 test05

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

    2. 配置 springDispatcherServlet-servlet.xml

    <!--配置一个统一异常处理-->
    <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--key:异常类型,arrEx对应的视图-->
                <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
                <prop key="java.lang.Exception">otherEx</prop>
            </props>
        </property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.otherEx.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>未知异常信息</title>
    </head>
    <body>
    <h1>朋友,系统发生了未知异常~, 请联系网站管理员</h1>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.异常处理的优先级梳理

    ● 异常处理的优先级

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

  • 相关阅读:
    全球电子商务交易预计将在2022年假日季增长15%,消费者情绪乐观
    英国公派访问学者带家属签证经验分享
    python--本地时间转UTC时间
    反欺诈模型常用开发工具
    javascript输出数据在控制台中console的常用方法(上)
    django Ajax(七)
    arm 内核版本编译记录
    12个MySQL慢查询的原因分析
    FPGA之旅设计99例之第十三例-----FPGA在OLED上显示DHT11数据
    AOP三剑客之Javassist
  • 原文地址:https://blog.csdn.net/qq_44981526/article/details/125458759