SpringMVC的異常處理和攔截器處理思路解析
SpringMVC的異常處理
異常處理思路:Controller 調(diào)用 service,service 調(diào)用 dao,異常都是向上拋出的,此時(shí) DispatcherServlet 會暫停正常的請求處理流程,轉(zhuǎn)而進(jì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ā)生的具體原因或詳細(xì)信息。當(dāng)創(chuàng)建 SysException 實(shí)例時(shí),會將具體的異常描述字符串傳入并賦值給 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直接強(qiáng)轉(zhuǎn)使用
if(e instanceof SysException){
exception = (SysException)e;
}else {
// 如果是其他異常,則包裝成一個新的SysException,并設(shè)置默認(rèn)提示信息
exception = new SysException("系統(tǒng)正在維護(hù),請聯(lián)系管理員");
}
// 存入異常提示信息
ModelAndView mv = new ModelAndView();
//Throwable定義了getMessage()方法,用于返回異常/錯誤的詳細(xì)描述信息
mv.addObject("errorMsg",e.getMessage());
// 設(shè)置跳轉(zhuǎn)的頁面
mv.setViewName("error");
return mv;
}
}如果捕獲的異常 e 是 SysException 類型,直接強(qiáng)轉(zhuǎn)使用;如果是其他異常,則包裝成一個新的SysException,并設(shè)置默認(rèn)提示信息 “系統(tǒng)正在維護(hù),請聯(lián)系管理員”,目的是將所有異常統(tǒng)一轉(zhuǎn)為SysException類型,便于后續(xù)統(tǒng)一處理,避免對不同異常類型寫重復(fù)代碼。通過 e.getMessage() 獲取異常的消息文本,將其存入模型中,指定異常發(fā)生時(shí)要跳轉(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 思想的組件,主要用于對處理器進(jìn)行預(yù)處理和后處理技術(shù)
攔截器(Filter)和過濾器(Interceptor)的功能比較類似,但有區(qū)別
- 技術(shù)歸屬與依賴
過濾器屬于Servlet 規(guī)范的一部分(定義在javax.servlet包中),不依賴任何框架,只要是 Servlet 容器(如Tomcat)就能運(yùn)行,可以通過web.xml或@WebFilter注解配置過濾器;
攔截器是 Spring 框架提供的組件(定義在org.springframework.web.servlet包中),依賴于 Spring 容器,僅在 Spring 環(huán)境中生效,其運(yùn)行需要 Spring 的 DispatcherServlet 調(diào)度,本質(zhì)是 Spring 對請求處理流程的增強(qiáng)。 - 作用范圍與處理對象
過濾器處理的是 ServletRequest 和 ServletResponse 對象,關(guān)注的是請求數(shù)據(jù)響應(yīng)數(shù)據(jù)本身,作用于整個請求、響應(yīng)周期,可以處理所有進(jìn)入 Servlet 容器的請求,覆蓋范圍更廣;
攔截器只攔截通過 DispatcherServlet 分發(fā)的請求,即處理的是 HandlerMethod(SpringMVC 的 Controller 方法),關(guān)注的是業(yè)務(wù)方法調(diào)用,范圍更窄。 - 執(zhí)行時(shí)機(jī)與生命周期
過濾器的生命周期由 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)建一個自定義攔截器類,需要實(shí)現(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í)行了...");
// 也是可以進(jìn)行頁面的跳轉(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é)成一條鏈,在訪問被攔截的方法時(shí),攔截器鏈中的攔截器會按著定義的順序執(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);
- 目標(biāo)業(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)文章
Java實(shí)現(xiàn)Promise.all()的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)Promise.all()的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
java 單例的五種實(shí)現(xiàn)方式及其性能分析
這篇文章主要介紹了java 單例的五種實(shí)現(xiàn)方式及其性能分析。的相關(guān)資料,需要的朋友可以參考下2017-07-07
Springboot下RedisTemplate的兩種序列化方式實(shí)例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過定義一個配置類,自定義RedisTemplate的序列化方式,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
java文字轉(zhuǎn)語音播報(bào)功能的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于java文字轉(zhuǎn)語音播報(bào)功能的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
java不同版本在多線程中使用隨機(jī)數(shù)生成器的實(shí)現(xiàn)
本文主要介紹了java不同版本在多線程中使用隨機(jī)數(shù)生成器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
基于Springboot一個注解搞定數(shù)據(jù)字典的實(shí)踐方案
這篇文章主要介紹了基于Springboot一個注解搞定數(shù)據(jù)字典問題,大致的方向是自定義注解,在序列化的時(shí)候進(jìn)行數(shù)據(jù)處理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
SpringBoot添加自定義攔截器的實(shí)現(xiàn)代碼
這篇文章主要介紹了SpringBoot添加自定義攔截器的實(shí)現(xiàn)代碼,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09

