![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iMhdDZhdzMiZWO5kDZyQTZyQTM4UjZyQGO5EGMhZGMy8CXxMzLcdDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL3M3Lc9CX6MHc0RHaiojIsJye.png)
spring mvc web開發中的異常處理
前面學習過struts2的異常處理,今天來看下spring mvc4的異常處理:
一、Servlet配置檔案修改
1 <bean id="exceptionResolver"
2 class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
3 <property name="defaultErrorView" value="errors/error" />
4 <property name="exceptionMappings">
5 <props>
6 <prop key="java.lang.Throwable">errors/error</prop>
7 </props>
8 </property>
9 </bean>
增加上面這一節,大意是:隻要有異常就跳到/WEB-INF/views/errors/error.jsp這個頁面,當然如果要實作個性化的錯誤處理,比如:業務錯誤跳到頁面A,SQL錯誤跳到頁面B...,直接在props節點下,根據不同的異常類型,自行擴充 (注:404之類的錯誤,仍然參考struts2異常進行中的做法,在web.xml中配置解決)
二、建立一個BaseController基類,裡面放一個以下方法:
1 @ExceptionHandler
2 public String exp(HttpServletRequest request, Exception ex) {
3 String resultViewName = "errors/error";
4
5 // 記錄日志
6 logger.error(ex.getMessage(), ex);
7
8 // 根據不同錯誤轉向不同頁面
9 if (ex instanceof BusinessException) {
10 resultViewName = "errors/biz-error";
11 } else {
12 // 異常轉換
13 ex = new Exception("系統太累了,需要休息!");
14 }
15 request.setAttribute("ex", ex);
16 return resultViewName;
17 }
記錄異常日志、根據不同的異常類型轉到不同的處理頁面、友好異常轉換(如果需要的話),都在上面的方法中處理了
三、所有Controller都繼承自BaseController
這個,就不解釋了
四、error.jsp頁面
1 <%@ page contentType="text/html;charset=UTF-8" language="java"%>
2 <%
3 Exception e = (Exception) request.getAttribute("ex");
4 %>
5 <html>
6 <head>
7 <title>師傅,有妖怪:error</title>
8 </head>
9 <body>
10 <H2>
11 錯誤:<%=e.getClass().getSimpleName()%></H2>
12 <hr />
13 <P>
14 <strong>錯誤描述:</strong><%=e.getMessage()%>
15 </P>
16
17 <P>
18 <strong>詳細資訊:</strong>
19 </P>
20 <pre>
21 <%
22 e.printStackTrace(new java.io.PrintWriter(out));
23 %>
24 </pre>
25 </body>
26 </html>
上面的内容隻是示意,大家可以根據需要自行美化
另:前文struts2的異常處理中,采用的是攔截器思想,spring mvc中也有攔截器,而且攔截的點更靈活:
1 package com.cnblogs.yjmyzz.interceptor;
2
3 import javax.servlet.http.HttpServletRequest;
4 import javax.servlet.http.HttpServletResponse;
5
6 import org.apache.logging.log4j.LogManager;
7 import org.apache.logging.log4j.Logger;
8 import org.springframework.web.servlet.ModelAndView;
9 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
10
11 public class ExcpetionInterceptor extends HandlerInterceptorAdapter {
12
13 protected Logger logger = LogManager.getLogger();
14
15 @Override
16 public boolean preHandle(HttpServletRequest request,
17 HttpServletResponse response, Object handler) throws Exception {
18 System.out.println("ExcpetionInterceptor.preHandle");
19 // 示範:限制僅允許從本機通路
20 if (request.getLocalAddr().equals("127.0.0.1")
21 || request.getLocalAddr().equals("0.0.0.0")) {
22 return true;
23 }
24 logger.error("非法入侵:" + request.getLocalAddr());
25 return false;
26 }
27
28 @Override
29 public void postHandle(HttpServletRequest request,
30 HttpServletResponse response, Object handler,
31 ModelAndView modelAndView) throws Exception {
32 System.out.println("ExcpetionInterceptor.postHandle");
33 }
34
35 @Override
36 public void afterCompletion(HttpServletRequest request,
37 HttpServletResponse response, Object handler, Exception ex)
38 throws Exception {
39 System.out.println("ExcpetionInterceptor.afterCompletion");
40 if (ex != null) {
41 logger.error(handler);
42 logger.error(ex.getMessage(), ex);
43 }
44 }
45
46 @Override
47 public void afterConcurrentHandlingStarted(HttpServletRequest request,
48 HttpServletResponse response, Object handler) throws Exception {
49 System.out
50 .println("ExcpetionInterceptor.afterConcurrentHandlingStarted");
51 }
52
53 }
攔截器建立後,依然要在servlet配置檔案中注冊:
<mvc:interceptors>
<bean class="com.cnblogs.yjmyzz.interceptor.ExcpetionInterceptor"></bean>
</mvc:interceptors>
spring mvc的攔截器提供了4個處理方法:
preHandle在Controller被調用前,先執行,可以在這裡執行一些安全檢查(上面示意了如何對IP做限制)
postHandle在Controller調用後執行,這時,可以修改ModelAndView,比如轉到其它view之類
afterCompletion在Controller調用全部完成後執行,如果ex變量不為空,表示有異常了,這裡可以記錄異常日志
afterConcurrentHandlingStarted這個沒怎麼研究過,暫時不做評價
值得一提的是:spring-mvc中的攔截器,雖然可以在afterCompletion中記錄異常日志,但如果按前面的baseController配合@ExceptionHandler做了處理,這裡的ex就變成了null,因為異常在前面已經得到了處理,是以這二種方法不推薦混用,另外afterCompletion方法中,如果要根據不同的異常類型轉到不同處理頁面,并不友善。