SpringMVC的異常處理和攔截器處理思路解析
SpringMVC的異常處理
異常處理思路:Controller 調(diào)用 service,service 調(diào)用 dao,異常都是向上拋出的,此時 DispatcherServlet 會暫停正常的請求處理流程,轉(zhuǎn)而進入異常處理流程,并將異常交給異常解析器處理。
controller代碼
package com.qcby.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/exception")
public class ExController {
/**
* try-catch 處理異常
*/
@RequestMapping("/findAll.do")
public String findAll1(){
try {
System.out.println("執(zhí)行了...");
// 模擬異常
int a = 10 / 0;
} catch (Exception e) {
e.printStackTrace();
//跳轉(zhuǎn)到友好提示頁面
}
return "suc";
}
/**
* 使用異常處理器方式
*/
@RequestMapping("/findAl2.do")
public String findAll2(){
System.out.println("執(zhí)行了...");
// 模擬異常
int a = 10/0;
return "suc";
}
}自定義異常類(與業(yè)務(wù)相關(guān)的異常),需要遵循JDK異常規(guī)范,繼承 Exception
package com.qcby.model;
public class SysException extends Exception {
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}message 是一個用于存儲異常信息的成員變量,作用是描述當(dāng)前異常發(fā)生的具體原因或詳細信息。當(dāng)創(chuàng)建 SysException 實例時,會將具體的異常描述字符串傳入并賦值給 message,之后可以通過 getMessage() 方法獲取這個信息,用于調(diào)試或向用戶展示錯誤原因。
自定義異常處理器,SpringMVC 提供了 HandlerExceptionResolver 異常處理的核心接口,定義了處理異常的規(guī)范
package com.qcby.util;
import com.qcby.model.SysException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 在控制臺打印異常的信息
e.printStackTrace();
// 聲明變量
SysException exception = null;
// 如果是自定義的SysException直接強轉(zhuǎn)使用
if(e instanceof SysException){
exception = (SysException)e;
}else {
// 如果是其他異常,則包裝成一個新的SysException,并設(shè)置默認提示信息
exception = new SysException("系統(tǒng)正在維護,請聯(lián)系管理員");
}
// 存入異常提示信息
ModelAndView mv = new ModelAndView();
//Throwable定義了getMessage()方法,用于返回異常/錯誤的詳細描述信息
mv.addObject("errorMsg",e.getMessage());
// 設(shè)置跳轉(zhuǎn)的頁面
mv.setViewName("error");
return mv;
}
}如果捕獲的異常 e 是 SysException 類型,直接強轉(zhuǎn)使用;如果是其他異常,則包裝成一個新的SysException,并設(shè)置默認提示信息 “系統(tǒng)正在維護,請聯(lián)系管理員”,目的是將所有異常統(tǒng)一轉(zhuǎn)為SysException類型,便于后續(xù)統(tǒng)一處理,避免對不同異常類型寫重復(fù)代碼。通過 e.getMessage() 獲取異常的消息文本,將其存入模型中,指定異常發(fā)生時要跳轉(zhuǎn)的視圖名稱為error,最終返回ModelAndView,Spring 會根據(jù)該對象渲染對應(yīng)的錯誤頁面并響應(yīng)給客戶端。
SpringMVC 基于組件式開發(fā),在 springmvc.xml 配置文件中配置異常處理器
<!--配置異常處理器-->
<bean id="sysExceptionResolver" class="com.qcby.util.SysExceptionResolver" />jsp頁面代碼
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>錯誤提示頁面</title>
</head>
<body>
?
<h3>錯誤的友好提示頁面</h3>
?
${errorMsg}
?
</body>
</html>效果展示:

SpringMVC框架中的攔截器
在 SpringMVC 中,攔截器是一種基于 AOP 思想的組件,主要用于對處理器進行預(yù)處理和后處理技術(shù)
攔截器(Filter)和過濾器(Interceptor)的功能比較類似,但有區(qū)別
- 技術(shù)歸屬與依賴
過濾器屬于Servlet 規(guī)范的一部分(定義在javax.servlet包中),不依賴任何框架,只要是 Servlet 容器(如Tomcat)就能運行,可以通過web.xml或@WebFilter注解配置過濾器;
攔截器是 Spring 框架提供的組件(定義在org.springframework.web.servlet包中),依賴于 Spring 容器,僅在 Spring 環(huán)境中生效,其運行需要 Spring 的 DispatcherServlet 調(diào)度,本質(zhì)是 Spring 對請求處理流程的增強。 - 作用范圍與處理對象
過濾器處理的是 ServletRequest 和 ServletResponse 對象,關(guān)注的是請求數(shù)據(jù)響應(yīng)數(shù)據(jù)本身,作用于整個請求、響應(yīng)周期,可以處理所有進入 Servlet 容器的請求,覆蓋范圍更廣;
攔截器只攔截通過 DispatcherServlet 分發(fā)的請求,即處理的是 HandlerMethod(SpringMVC 的 Controller 方法),關(guān)注的是業(yè)務(wù)方法調(diào)用,范圍更窄。 - 執(zhí)行時機與生命周期
過濾器的生命周期由 Servlet 容器管理,隨容器啟動而初始化,隨容器關(guān)閉而銷毀;
攔截器的生命周期由 Spring 容器管理,隨 Spring 上下文初始化而創(chuàng)建,隨上下文銷毀而銷毀。
自定義攔截器步驟
首先需要定義一個controller層的方法用于測試
@Controller
@RequestMapping("/dept")
public class DeptController {
/**
* 測試方法
* @return
*/
@RequestMapping("/findAll.do")
public String findAll() {
System.out.println("controller方法執(zhí)行了...");
return "suc";
}
}創(chuàng)建一個自定義攔截器類,需要實現(xiàn)HandlerInterceptor接口,重寫接口內(nèi)需要的方法
package com.qcby.util;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
/**
* 攔截controller中方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor的preHandle方法執(zhí)行了...");
// 手動跳轉(zhuǎn)頁面
// request.getRequestDispatcher("/WEB-INF/pages/suc.jsp").forward(request,response);
// 攔截,不放行
//return false;
// 放行,執(zhí)行controller中方法
return true;
}
}需要在 springmvc.xml 配置文件中配置攔截器,并指定攔截 / 排除的路徑
<!--配置攔截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--該攔截器攔截哪些資源-->
<mvc:mapping path="/dept/**"/>
<!--哪些資源不攔截
<mvc:exclude-mapping path="" />
-->
<!--攔截器對象-->
<bean class="com.qcby.util.MyInterceptor1" />
</mvc:interceptor>
</mvc:interceptors>請求路徑:http://localhost:8080/dept/findAll.do
打印結(jié)果如圖:

即 MyInterceptor 攔截器生效了
請求處理的大致流程為:客戶端請求 → 過濾器 → Servlet容器 → DispatcherServlet → 攔截器 → Controller → 攔截器 → DispatcherServlet → 過濾器 → 客戶端響應(yīng)
攔截器圍繞 Controller 方法的調(diào)用,在 HandlerInterceptor 接口中的有三個核心方法:
- preHandle:是在 Controller 方法執(zhí)行前攔截的方法,可以使用request或者response跳轉(zhuǎn)到指定的頁面;
- return true 放行,執(zhí)行下一個攔截器,如果沒有攔截器,則執(zhí)行controller中的方法;return false 不放行,會終止后續(xù)流程,包括controller中的方法與后續(xù)攔截器;
- postHandle:是在 Controller 方法執(zhí)行之后、視圖渲染之前攔截的方法,可以使用request、response的方式或者通過 return "xxx"指定了要跳轉(zhuǎn)的視圖,若指定了跳轉(zhuǎn)的頁面那么跳轉(zhuǎn)操作會覆蓋 Controller 原本指定的頁面,Controller 方法所要跳轉(zhuǎn)的頁面將不會顯示;
- afterCompletion:是在整個請求處理完成(視圖渲染后)攔截的方法,request或者response不能再跳轉(zhuǎn)頁面了。
package com.qcby.util;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 攔截controller中的方法
*/
//controller方法執(zhí)行前攔截的方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
System.out.println("MyInterceptor1的preHandler方法執(zhí)行了");
// 手動跳轉(zhuǎn)頁面
// request.getRequestDispatcher("/WEB-INF/pages/suc.jsp").forward(request,response);
// 攔截,不放行
//return false;
// 放行,執(zhí)行方法
return true;
}
//controller方法執(zhí)行后攔截的方法
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception{
System.out.println("MyInterceptor1的postHandle方法執(zhí)行了...");
// 也是可以進行頁面的跳轉(zhuǎn)
request.getRequestDispatcher("/WEB-INF/pages/suc.jsp").forward(request,response);
return;
}
//controller跳轉(zhuǎn)的jsp頁面都執(zhí)行完成了,最后執(zhí)行該方法
@Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex)throws Exception{
System.out.println("MyInterceptor1的afterCompletion方法執(zhí)行了...");
}
}配置多個攔截器
可以定義攔截器鏈,攔截器鏈就是將攔截器按著一定的順序結(jié)成一條鏈,在訪問被攔截的方法時,攔截器鏈中的攔截器會按著定義的順序執(zhí)行
我們再定義一個攔截器
package com.qcby.util;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor2 implements HandlerInterceptor {
/**
* 攔截controller中方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor2的preHandle方法執(zhí)行了...");
// 放行,執(zhí)行controller中方法
return true;
}
/**
* controller方法執(zhí)行后,要攔截的方法
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2的postHandle方法執(zhí)行了...");
}
/**
* controller跳轉(zhuǎn)的jsp頁面都執(zhí)行完成了,最后執(zhí)行該方法
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor2的afterCompletion方法執(zhí)行了...");
}
}配置兩個攔截器
<mvc:interceptors>
<!--配置攔截器-->
<mvc:interceptor>
<!--該攔截器攔截哪些資源-->
<mvc:mapping path="/dept/**"/>
<!--哪些資源不想攔截
<mvc:exclude-mapping path="" />
-->
<!--攔截器對象-->
<bean class="com.qcby.util.MyInterceptor1" />
</mvc:interceptor>
<!--配置攔截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.qcby.util.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
總結(jié)執(zhí)行順序:
- 請求前階段(preHandle):按攔截器注冊順序執(zhí)行(MyInterceptor1→MyInterceptor2);
- 目標業(yè)務(wù)(Controller):僅在所有攔截器的 preHandle 都返回 true 后才執(zhí)行;
- 響應(yīng)后階段(postHandle、afterCompletion):按注冊逆序執(zhí)行(MyInterceptor2→MyInterceptor1)。
到此這篇關(guān)于SpringMVC的異常處理和攔截器的文章就介紹到這了,更多相關(guān)SpringMVC異常處理和攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于@RequestParam和@RequestBody的用法及說明
本文主要介紹了Spring框架中參數(shù)綁定相關(guān)的三個注解:@RequestParam、@ModelAttribute和@RequestBody的使用場景和區(qū)別,通過比較它們的使用場景和注意事項,幫助讀者快速理解并正確使用這些注解,避免在項目開發(fā)中出現(xiàn)常見問題2026-03-03
實例解析Json反序列化之ObjectMapper(自定義實現(xiàn)反序列化方法)
這篇文章主要介紹了實例解析Json反序列化之ObjectMapper,json自定義序列化的方法,需要的朋友可以了解下。2017-09-09
SpringBoot調(diào)用公共模塊的自定義注解失效的解決
這篇文章主要介紹了SpringBoot調(diào)用公共模塊的自定義注解失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java分支循環(huán)與數(shù)組核心知識點總結(jié)
這篇文章主要介紹了Java分支循環(huán)與數(shù)組核心知識點總結(jié)的相關(guān)資料,理解循環(huán)與分支結(jié)構(gòu)是掌握編程的基礎(chǔ),重復(fù)執(zhí)行操作的循環(huán)和基于條件的執(zhí)行路徑分支有助于構(gòu)建復(fù)雜的程序邏輯,需要的朋友可以參考下2026-01-01
Java 基于TCP Socket 實現(xiàn)文件上傳
這篇文章主要介紹了Java 基于TCP Socket 實現(xiàn)文件上傳的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12

