1. 程式人生 > >SpringMVC框架08—統一異常處理

SpringMVC框架08—統一異常處理

value 視圖解析 業務 完全 style lte 實現類 png div

前言

在Spring MVC 應用的開發中,不管是對底層數據庫操作,還是業務層或控制層操作,都會不可避免地遇到各種可預知的、不可預知的異常需要處理。如果每個過程都單獨處理異常,那麽系統的代碼耦合度高,工作量大且不好統一,以後維護的工作量也很大。
在Spring MVC中提供了三種統一異常處理的方式,能夠將所有類型的異常處理從各層中解耦出來,這樣既保證了相關處理過程的功能單一,又實現了異常信息的統一處理和維護。

1、演示案例準備

為了驗證Spring MVC 框架的3中異常處理方式,需要編寫一個測試的應用,從Dao層、Service層、Controller層分別拋出不同的異常。本教程指定了3個異常,分別是:SQLException、自定義異常和未知異常,然後分別集成3種方式進行異常處理。

分別創建相應的包和類,如下圖所示:

技術分享圖片

(1)創建自定義異常類MyException

代碼示例:

package com.demo.exception;

public class MyException extends Exception {
    public MyException() {
    }

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

(2)創建Dao層異常類

代碼示例:

package com.demo.dao;

import
com.demo.exception.MyException; import org.springframework.stereotype.Repository; import java.sql.SQLException; @Repository("testExceptionDao") public class TestExceptionDao { public void daodb() throws SQLException { throw new SQLException("Dao中數據庫異常"); } public void
daomy() throws MyException { throw new MyException("Dao中自定義異常"); } public void daono() throws Exception { throw new Exception("Dao中的未知異常"); } }

(3)創建Service層異常類

在service包下創建TestExceptionService接口和TestExceptionServiceImpl實現類,在該接口中定義6個方法,其中有3個是調用Dao層,有3個是Service層的方法。
TestExceptionService接口代碼示例:

package com.demo.service;

public interface TestExceptionService {
    public void servicedb() throws Exception;
    public void servicemy() throws Exception;
    public void serviceno() throws Exception;
    public void daodb() throws Exception;
    public void daomy() throws Exception;
    public void daono() throws Exception;
}

TestExceptionServiceImpl實現類代碼示例:

package com.demo.service;

import com.demo.dao.TestExceptionDao;
import com.demo.exception.MyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;

@Service("testExceptionService")
public class TestExceptionServiceImpl implements TestExceptionService {

    @Autowired
    private TestExceptionDao testExceptionDao;

    @Override
    public void servicedb() throws Exception {
        throw new SQLException("Service中數據庫異常");
    }

    @Override
    public void servicemy() throws Exception {
        throw new MyException("Service中自定義異常");
    }

    @Override
    public void serviceno() throws Exception {
        throw new Exception("Service中未知異常");
    }

    @Override
    public void daodb() throws Exception {
        testExceptionDao.daodb();
    }

    @Override
    public void daomy() throws Exception {
        testExceptionDao.daomy();
    }

    @Override
    public void daono() throws Exception {
        testExceptionDao.daono();
    }

}

(4)創建控制器層異常類

代碼示例:

package com.demo.controller;

import com.demo.exception.MyException;
import com.demo.service.TestExceptionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.sql.SQLException;

@Controller
public class TestExceptionController {
    @Autowired
    private TestExceptionService testExceptionService;

    @RequestMapping("/db")
    public void db() throws SQLException {
        throw new SQLException("控制器中數據庫異常");
    }

    @RequestMapping("/my")
    public void my() throws MyException {
        throw new MyException("控制器中自定義異常");
    }

    @RequestMapping("no")
    public void no() throws Exception {
        throw new Exception("控制器中未知異常");
    }

    @RequestMapping("/servicedb")
    public void servicedb() throws Exception {
        testExceptionService.servicedb();
    }

    @RequestMapping("/servicemy")
    public void servicemy() throws Exception {
        testExceptionService.servicemy();
    }

    @RequestMapping("/serviceno")
    public void serviceno() throws Exception {
        testExceptionService.serviceno();
    }

    @RequestMapping("/daodb")
    public void daodb() throws Exception {
        testExceptionService.daodb();
    }

    @RequestMapping("/daomy")
    public void daomy() throws Exception {
        testExceptionService.daomy();
    }

    @RequestMapping("/daono")
    public void daono() throws Exception {
        testExceptionService.daono();
    }

}

(5)創建View視圖層

View層共有5個JSP頁面,分別是:
測試應用首頁index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<body>
<h1>演示案例</h1>
<p>
    <a href="${pageContext.request.contextPath}/daodb">處理dao中數據庫異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/daomy">處理dao中自定義異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/daono">處理dao中未知異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/servicedb">處理service中數據庫異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/servicemy">處理service中自定義異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/serviceno">處理service中未知異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/db">處理controller中數據庫異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/my">處理controller中自定義異常</a>
</p>
<p>
    <a href="${pageContext.request.contextPath}/no">處理controller中未知異常</a>
</p>
</body>
</html>

404錯誤對應頁面404.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>404</title>
</head>
<body>
    <h2>資源不存在</h2>
</body>
</html>

未知異常對應頁面error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>未知異常</title>
</head>
<body>
    <h1>未知錯誤:</h1>
    <%=exception%>
    <h2>錯誤內容:</h2>
    <%
        exception.printStackTrace(response.getWriter());
    %>
</body>
</html>

自定義異常對應頁面my-error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>自定義異常</title>
</head>
<body>
    <h1>自定義異常錯誤:</h1>
    <%=exception%>
    <h2>錯誤內容:</h2>
    <%
        exception.printStackTrace(response.getWriter());
    %>
</body>
</html>

SQL異常對應頁面sql-error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>數據庫異常</title>
</head>
<body>
    <h1>數據庫異常錯誤:</h1>
    <%=exception%>
    <h2>錯誤內容:</h2>
    <%
        exception.printStackTrace(response.getWriter());
    %>
</body>
</html>

(6)配置全局異常處理

在web.xml中配置全局異常404處理

<!--配置全局異常-->
<error-page>
  <error-code>404</error-code>
  <location>/404.jsp</location>
</error-page>

2、使用配置統一處理異常

在springmvc.xml中配置org.springframework.web.servlet.handler.SimpleMappingExceptionResolver類,並且要提前配置異常類和View的對應關系。
springmvc.xml配置代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <!--將AnnotationHandler自動掃描到IOC容器中-->
    <context:component-scan base-package="com"></context:component-scan>

    <!--配置視圖解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前綴-->
        <property name="prefix" value="/"></property>
        <!--配置後綴-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--配置異常相關-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--定義默認的異常處理頁面,當該異常類型註冊時使用-->
        <property name="defaultErrorView" value="error"></property>
        <!--定義異常處理頁面用來獲取異常信息的變量名,默認名為exception-->
        <property name="exceptionAttribute" value="ex"></property>
        <!--定義需要特殊處理的異常,用類名或完全路徑名作為key,異常頁名作為值-->
        <property name="exceptionMappings">
            <props>
                <prop key="com.demo.exception.MyException">my-error</prop>
                <prop key="java.sql.SQLException">sql-error</prop>
                <!--在這裏還可以繼續擴展對不同異常類型的處理-->
            </props>
        </property>
    </bean>

</beans>

演示效果:

技術分享圖片

404演示效果:

技術分享圖片

3、使用接口統一處理異常

org.springframework.web.servlet.HandlerExceptionResolver 接口用於解析請求處理過程中所產生的異常。在exception包中創建一個HandlerExceptionResolver接口的實現類MyExceptionHandler,代碼如下:

package com.demo.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) {
        Map<String,Object> model = new HashMap<String,Object>();
        model.put("ex",e);
        //根據不同錯誤轉向不同頁面(統一處理),即異常與View的對應關系
        if (e instanceof MyException) {
            return new ModelAndView("my-error",model);
        }else if (e instanceof SQLException) {
            return new ModelAndView("sql-error",model);
        }else {
            return new ModelAndView("error",model);
        }
    }
}

在springmvc.xml文件中配置實現類MyExceptionHandler的托管

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <!--將AnnotationHandler自動掃描到IOC容器中-->
    <context:component-scan base-package="com"></context:component-scan>

    <!--配置視圖解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前綴-->
        <property name="prefix" value="/"></property>
        <!--配置後綴-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--托管MyExceptionHandler-->
    <bean class="com.demo.exception.MyExceptionHandler"></bean>

</beans>

4、使用註解統一處理異常

在controller包下創建BaseController類,並在該類的方法中使用@ExceptionHandler註解聲明異常處理方法,代碼如下:

package com.demo.controller;

import com.demo.exception.MyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;

public abstract class BaseController {

    @ExceptionHandler
    public String exception(HttpServletRequest request, HttpServletResponse response,Exception e){
        request.setAttribute("ex",e);
        if (e instanceof MyException) {
            return "my-error";
        } else if (e instanceof SQLException) {
            return "sql-error";
        } else {
            return "error";
        }
    }
}

將所有需要異常處理的Controller都繼承BaseController類,示例代碼如下:

@Controller
public class TestExceptionController extends BaseController{
    //...
}

SpringMVC框架08—統一異常處理