手记

SpringMVC中的异常(七)

基本概念

        在Java中,异常的体系结构是:

Throwable

    Error

        OutOfMemoryError

    Exception

        IOException

            FileNotFoundException

        SQLException

        UnsupportedEncodingException

        RuntimeException

            NullPointerException

            ClassCastException

            ArithmeticException

            IllegalArgumentException

            IndexOutOfBoundsException

                ArrayIndexOutOfBoundsException

        在这些异常中,RuntimeException及其子孙类异常,在Java语法中并不要求必须处理!主要的原因有:这些异常出现的频率可能非常高,如果一定要处理,例如try...catch,则几乎所有的代码都需要放在try代码块中!并且,这些异常是可以杜绝的异常,通过严谨的编程,可以保证这些异常绝对不会出现!

        处理异常有2种方式:使用try...catch处理异常,或者使用throw抛出异常对象,并且在方法的声明中使用throws语法声明抛出!

        通常,异常都是必须处理的,如果没有处理异常,会导致异常不断向上抛出,最终,Java EE中的异常会由Tomcat来处理,会把跟踪日志显示在页面中!而这些跟踪日志是普通用户看不懂的,对于专业人士而言,还可能分析出项目中的一些内容,对于后续解决问题也没有任何帮助,所以,从开发原则上来说,必须处理异常!

处理异常-SimpleMappingExceptionResolver

        非RuntimeException是从语法上强制要求处理的。所以,每次调用了抛出异常的方法,就必须try...catch或继续声明抛出!

        而RuntimeException及其子孙类异常并不从语法上强制要求处理。但是,一旦出现,会影响项目的安全、用户体验等各方面都会产生负面影响!

        SpringMVC提供了统一处理异常的方式:使用SimpleMappingExceptionResolver类,该类通过private Properties exceptionMappings;属性来配置异常种类与转发到的页面的对应关系,即每一种异常都可以有一个与之对应的页面,一旦项目中出现配置过的异常,就会自动转发到对应的页面来提示错误信息!

<!-- 配置统一处理异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">   
     <property name="exceptionMappings">       
         <props>            
              <prop key="java.lang.RuntimeException">runtime</prop>           
              <prop key="java.lang.NullPointerException">null</prop>            
              <prop key="java.lang.ArrayIndexOutOfBoundsException">index</prop>        
         </props>   
    </property>
</bean>

        这种方式处理异常非常的简单,但是,也有一个比较大的问题:无法提示更详细的信息!例如出现ArrayIndexOutOfBoundsException时,其实,异常对象中还封装了详细的错误信息,即:越界值是多少。

处理异常-@ExceptionHandler

        可以在控制器类中自定义一个处理异常的方法,在方法之前添加@ExceptionHandler,然后,凡是约定范围内的异常,都可以由该方法来决定如何处理:

@ExceptionHandler
public String handleException(Exception e) {   
     System.out.println(        "ExceptionController.handleException()");     
     if (e instanceof NullPointerException) {      
        return "null";   
         } else if (e instanceof ArrayIndexOutOfBoundsException) {     
            return "index";   
             }   
    return "runtime";
    }

        在处理异常时,如果需要转发数据,可以将返回值修改为ModelAndView,或者,在处理异常的方法参数列表中添加HttpServletRequest,但是,却不可以使用ModelMap来封装转发的数据:

@ExceptionHandler
public String handleException(Exception e,        HttpServletRequest request) {   
     System.out.println(        "ExceptionController.handleException()");     
     if (e instanceof NullPointerException) {       
          return "null";    
          } else if (e instanceof ArrayIndexOutOfBoundsException) {        
          request.setAttribute("msg", e.getMessage());        
          return "index";   
           }    
     return "runtime";
           }

思考:是否可以在类中添加3个处理异常的方法,分别只处理NullPointerException和ArrayIndexOutOfBoundsException和RuntimeException?

答案:可以!允许使用多个不同的方法来处理异常!

思考:假设处理异常的方法在A控制器中,而B控制器中处理请求时出现异常,会被处理吗?

答案:不可以!通常,可以创建一个BaseController,作为当前项目的控制器类的基类,然后,把处理异常的代码编写在这个类中即可!

关于@ExceptionHandler,还可以用于限制其对应的方法处理的异常的种类!例如:

@ExceptionHandler(IndexOutOfBoundsException.class)

以上代码表示接下来的方法只处理IndexOutOfBoundsException及其子孙类异常,而其它的异常并不处理!

小结

        处理异常的本质并不可以“撤消”异常,无法让已经出现的问题还原到没有问题!所以,处理异常的本质应该是尽量的补救已经出现的问题,并且,尝试通过提示信息等方式告之使用者,尽量执行正确的操作,避免后续再次出现同样的问题!

        并且,对于开发者而言,应该处理每一个可能出现的异常,因为,如果没有处理,就会继续抛出,最终被Tomcat捕获,而Tomcat处理的方式是把异常的跟踪信息显示在页面上,是极为不合适的!

        在SpringMVC中,处理异常有2种方式,第1种是通过SimpleMappingExceptionResolver设置异常与转发目标的对应关系,第2种是使用@ExceptionHandler为处理异常的方法进行注解,推荐使用第2种方式。

        通常,会把处理异常的方法写在项目的控制器类的基类中,即写在BaseController中,在使用@ExceptionHandler时,可以明确的指定所处理的异常类型,例如:@ExceptionHandler(NullPointerException),且,在控制器类中,可以编写多个处理异常的方法!

    关于处理异常的方法,应该是public方法,返回值类型与处理请求的方式相同,可以是String或ModelAndView或其它允许的类型,方法的名称可以自定义,参数列表中必须包括Exception类型的参数,还允许存在HttpServletRequest、HttpServletResponse,不允许使用ModelMap。


1人推荐
随时随地看视频
慕课网APP