SpringMVC框架08—統一異常處理
前言
在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; importcom.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 voiddaomy() 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—統一異常處理