(01)Spring MVC之处理异常的两种方式及优先级

  项目开发中异常需要统一处理,总的来说有两种方式,一种是实现HandlerExceptionResolver接口,一种是使用@ExceptionHandler注解的方式。其中Spring已经为我们提供了一个实现了HandlerExceptionResolver接口的类SimpleMappingExceptionResolver,有人把它单独列为一种方式,不过我认为方式越少越好,哈哈哈哈哈,下面记录一下Spring MVC处理异常的这两种方式。

  1、实现HandlerExceptionResolver接口

  1)在spring-context.xml中配置默认的实现类SimpleMappingExceptionResolver

<!-- 默认处理异常 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->  
        <property name="defaultErrorView" value="page/error/defaultError"/>  
        <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->  
            <property name="exceptionAttribute" value="ex"/>  
        <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,将不同的异常映射到不同的页面上。-->  
        <property name="exceptionMappings">  
               <props>  
                <prop key="IOException">page/error/ioexp</prop>  
                <prop key="java.sql.SQLException">page/error/sqlexp</prop>  
            </props>  
        </property>  
    </bean> 

  新建测试类:TestExceptionController.java

package com.sl.controller;

import java.io.IOException;
import java.sql.SQLException;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/testException")
public class TestExceptionController {
    
    public static Logger logger=Logger.getLogger(TestExceptionController.class);
    
    @RequestMapping("/iOException")
    @ResponseBody
    public void getException1() throws IOException {
        throw new IOException("测试抛出io异常"); 
    }
    
    @RequestMapping("/sQLException")
    @ResponseBody
    public void getException2() throws SQLException{
        throw new SQLException("测试抛出sql异常");
    }
    
    @RequestMapping("/runtimeException")
    @ResponseBody
    public void getRuntimeException(){
        throw new RuntimeException("测试抛出runtimeException异常");
    }
    
    @RequestMapping("/classNotFoundException")
    @ResponseBody
    public void getClassNotFoundException(){
        try {
            throw new ClassNotFoundException("测试捕获classNotFoundException异常");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

  新建IO异常跳转页面:page/error/ioexp.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="java.lang.Exception" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>IO异常页面</title>
</head>
<body>
IO异常页面<br>
    <% Exception ex = (Exception)request.getAttribute("ex"); %>
    <H2>Exception: <%= ex.getMessage()%></H2>
    <P/>
    <% ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
</html>

  新建SQL异常跳转页面:page/error/sqlexp.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="java.lang.Exception" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>默认异常页面</title>
</head>
<body>
sql异常页面<br>
    <% Exception ex = (Exception)request.getAttribute("ex"); %>
    <H2>Exception: <%= ex.getMessage()%></H2>
    <P/>
    <% ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
</html>

  新建默认异常跳转页面,没有指定跳转页面的异常跳转到该页面:page/error/defaultError.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="java.lang.Exception" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>默认异常页面</title>
</head>
<body>
默认异常页面<br>
    <% Exception ex = (Exception)request.getAttribute("ex"); %>
    <H2>Exception: <%= ex.getMessage()%></H2>
    <P/>
    <% ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
</html>

  测试:http://localhost:8080/spring-web/testException/iOException

   测试:http://localhost:8080/spring-web/testException/sQLException

  测试:http://localhost:8080/spring-web/testException/runtimeException

  测试:http://localhost:8080/spring-web/testException/classNotFoundException,捕获异常,不会跳转页面

  2)自定义实现HandlerExceptionResolver接口

  新建类:CustomExceptionHandler

package com.core.exception;

import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

@Component
public class CustomExceptionHandler implements HandlerExceptionResolver{

    @Override  
    public ModelAndView resolveException(HttpServletRequest request,  
            HttpServletResponse response, Object object, Exception exception) {
        
        request.setAttribute("ex",exception);
        if(exception instanceof IOException){
            return new ModelAndView("page/error/ioexp2");  
        }else if(exception instanceof SQLException){  
            return new ModelAndView("page/error/sqlexp2");  
        }
        return new ModelAndView("page/error/defaultError2"); 
    }  
}

  新建IO异常跳转页面:page/error/ioexp2.jsp,内容一样,“IO异常页面”字样改为“IO异常页面_2”以示区别。

  新建SQL异常跳转页面:page/error/sqlexp2.jsp,内容一样,“sql异常页面”字样改为“sql异常页面_2”以示区别。

  新建默认异常跳转页面:page/error/defaultError2.jsp,内容一样,“默认异常页面”字样改为“默认异常页面_2”以示区别。

  测试:http://localhost:8080/spring-web/testException/iOException

  测试:http://localhost:8080/spring-web/testException/sQLException 

  测试:http://localhost:8080/spring-web/testException/runtimeException

  测试:http://localhost:8080/spring-web/testException/classNotFoundException,捕获异常,不会跳转页面 

  2、使用@ExceptionHandler注解方式

  1)测试在本Controller中抛出异常统一处理

  新建测试类 ExceptionHandlerController.java

package com.core.exception;

import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/test")
public class ExceptionHandlerController {
    
    @ExceptionHandler(value={Exception.class})  
    public String exp3(Exception ex,HttpServletRequest request) {  
        request.setAttribute("ex", ex);  
        return "page/error/defaultError3";  
    }

    @ExceptionHandler(value={IOException.class})  
    public String exp(Exception ex,HttpServletRequest request) {  
        request.setAttribute("ex", ex);  
        return "page/error/ioexp3";  
    }
    
    @ExceptionHandler(value={SQLException.class})  
    public String exp2(Exception ex,HttpServletRequest request) {  
        request.setAttribute("ex", ex);  
        return "page/error/sqlexp3";   
    } 
    
    @RequestMapping("/iOException")
    @ResponseBody
    public void getException1() throws IOException {
        throw new IOException("测试抛出io异常"); 
    }
    
    @RequestMapping("/sQLException")
    @ResponseBody
    public void getException2() throws SQLException{
        throw new SQLException("测试抛出sql异常");
    }
    
    @RequestMapping("/runtimeException")
    @ResponseBody
    public void getRuntimeException(){
        throw new RuntimeException("测试抛出runtimeException异常");
    }
    
    @RequestMapping("/classNotFoundException")
    @ResponseBody
    public void getClassNotFoundException(){
        try {
            throw new ClassNotFoundException("测试捕获classNotFoundException异常");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

  新建IO异常跳转页面:page/error/ioexp3.jsp,内容一样,“IO异常页面”字样改为“IO异常页面_3”以示区别。

  新建SQL异常跳转页面:page/error/sqlexp3.jsp,内容一样,“sql异常页面”字样改为“sql异常页面_3”以示区别。

  新建默认异常跳转页面:page/error/defaultError3.jsp,内容一样,“默认异常页面”字样改为“默认异常页面_3”以示区别。

  测试:http://localhost:8080/spring-web/test/iOException

  测试:http://localhost:8080/spring-web/test/sQLException

  测试:http://localhost:8080/spring-web/test/runtimeException

  测试:http://localhost:8080/spring-web/test/classNotFoundException

  2)测试其它Controller中抛出异常统一处理 

  测试:http://localhost:8080/spring-web/testException/iOException ,发现统一异常处理没起作用

  如果用注解的方式处理全局的异常,需要在处理异常所在的Controller上添加注解@ControllerAdvice

  下面修改 ExceptionHandlerController.java,添加注解@ControllerAdvice

@ControllerAdvice
@Controller
@RequestMapping("/test")
public class ExceptionHandlerController
... ...

  测试外部Controller抛出的异常:http://localhost:8080/spring-web/testException/iOException

  测试内部Controller抛出的异常:http://localhost:8080/spring-web/test/iOException

  3、测试异常优先级

  新增加一种处理异常页面跳转的方式,在web.xml里面配置异常与页面跳转的关系,如下:

    <!-- 没有全局异常处理的,则跳到该页面,页面不要放在WEB-INF下,否则无法访问到 -->
      <error-page>
        <error-code>500</error-code>
        <location>/500.jsp</location>
    </error-page>
    <error-page> 
          <exception-type>java.lang.Throwable</exception-type> 
          <location>/500.jsp</location> 
    </error-page> 
    <error-page> 
          <error-code>404</error-code> 
          <location>/404.jsp</location> 
    </error-page> 

  新建页面:src/main/webapp/404.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="java.lang.Exception" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>404异常页面</title>
</head>
<body>
    <H2>这是404异常页面</H2>
</body>
</html>

  新建页面:src/main/webapp/500.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="java.lang.Exception" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>500异常页面</title>
</head>
<body>
    <H2>这是500异常页面</H2>
</body>
</html>

  下面测试一下这几种异常跳转页面的优先级,我们细分了5种:

  a、配置了SimpleMappingExceptionResolver

  b、自定义了HandlerExceptionResolver的实现类CustomExceptionHandler

  c、新建类ExceptionHandlerController,使用注解@ExceptionHandler处理该类的内部异常

  d、新建类ExceptionHandlerController,使用注解@ExceptionHandler和@ControllerAdvice处理所有类的异常

  e、web.xml中配置异常码或异常类与错误页面的关系

  下面开始测试:

  首先找满足5种情况的测试:http://localhost:8080/spring-web/test/iOException,进入到io异常页面_3,可见内部加注解方式优先级最高。

  然后找除了上面的一种,只满足4条的测试:http://localhost:8080/spring-web/testException/iOException,外部全局注解的方式第2

  然后找除了上面的两种,只满足3条的测试,去掉ExceptionHandlerController的@ControllerAdvice注解,执行下面的测试

  http://localhost:8080/spring-web/testException/iOException ,进入到IO异常页面_2,可见自定义异常实现接口第3。

  然后找除了上面的三种,只满足2条的测试,去掉自定义异常CustomExceptionHandler的注解@Component,执行下面的测试,

  http://localhost:8080/spring-web/testException/iOException ,进入到IO异常页面,可见SimpleMappingExceptionResolver第4。

  然后找除了上面的四种,只满足1条的测试,去掉spring-context.xml中配置的异常SimpleMappingExceptionResolver,执行下面的测试,

  http://localhost:8080/spring-web/testException/iOException ,进入到500页面,可见web.xml中的配置第5。

  最后,去掉web.xml中的配置,再次测试http://localhost:8080/spring-web/testException/iOException,出现了tomcat经典500异常界面:

  综上所述:

  使用@ExceptionHandler处理本Controller内部异常优先级最高;

  使用@ExceptionHandler+@ControllerAdvice处理外部Controller异常优先级第二; 

  自定义了实现了HandlerExceptionResolver接口的类优先级第三;

  spring-context.xml中配置SimpleMappingExceptionResolver优先级第四;

  web.xml配置error-page优先级第五;

  不做任何处理,会跳转到tomcat默认的异常页面;

 

  

   

 

posted @ 2020-04-10 11:39  雷雨客  阅读(1199)  评论(0编辑  收藏  举报