Java框架之SpringMVC 05-攔截器-異常對映-Spring工作流程
SpringMVC
攔截器
Spring MVC也可以使用攔截器對請求進行攔截處理,可以自定義攔截器來實現特定的功能,自定義的攔截器可以實現HandlerInterceptor介面中的三個方法,也可以繼承HandlerInterceptorAdapter 介面卡類按照需要那個方法,就實現哪個方法
過濾器與攔截器區別
過濾器:過濾器在Servlet之前操作
攔截器:攔截器在Servlet之後,請求處理器(Controller)之前操作。
攔截器三個方法
① preHandle():這個方法在(Controller)處理器處理請求之前被呼叫,在該方法中對使用者請求 request 進行處理。如果該攔截器對請求進行攔截處理後還要呼叫其他的攔截器,或者是業務處理器去進行處理,則返回 true;如果不需要再呼叫其他的元件去處理請求,則返回false。(如果返回false則後續操作都不再執行,類似於過濾器的 doFilter 所以正常情況下不要返回 false)
② postHandle():這個方法在(Controller)處理器處理完請求後,但是 DispatcherServlet 向客戶端返回響應前(在檢視渲染之前)被呼叫,在該方法中對使用者請求request進行處理。
③ afterCompletion():這個方法在 DispatcherServlet 完全處理完請求後(轉發|重定向 之後)被呼叫,可以在該方法中進行一些資源清理關閉的操作。
配置攔截器
<mvc:interceptors> <!-- 為所有請求設定攔截器 也可用 ref 引用已經裝配好的攔截器--> <bean id="firstHandlerInterceptor" class="main.controller.FirstHandlerInterceptor"></bean> <mvc:interceptor> <!-- 表示指定攔截器只攔截/test/下的所有請求--> <mvc:mapping path="/test/**/"/> <!-- 表示訪問/test/test.do的請求不會觸發攔截器--> <mvc:exclude-mapping path="/test/test.do"/> <!-- 為指定的請求設定攔截器 也可用 ref 引用已經裝配好的攔截器--> <bean id="testInterceptor" class="main.controller.TestInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
程式執行順序
1. preHandle():執行請求處理器的請求(Controller)方法之前執行。
2. 執行請求處理器的請求(Controller)方法
3. postHandle():執行請求處理器的請求(Controller)方法之後,在檢視渲染之前。
4. 檢視渲染
5. afterCompletion():檢視渲染(轉發|重定向)之後執行。
多個攔截器的執行流程
當存在多個攔截器時的執行順序,由配置的先後順序決定。(preHandle() 先配置,先執行)
preHandle():與攔截器配置的先後順序一致。
postHandle():與攔截器配置的先後順序相反。底層倒序迴圈呼叫的
afterCompletion():與攔截器配置的先後順序相反。
preHandle()返回值為false時的工作原理
第一個攔截器的preHandle()的返回值為false:
只執行第一個攔截器的prehandle()方法,執行完,return;(後續的方法都不執行)
不是第一個攔截器的preHandle()的返回值為false:
當前攔截器之前的攔截器的afterCompletion()都會被執行。
當兩個攔截器的 preHandle() 方法都返回 true 時,按照虛線路執行
當第二個攔截器 preHandle() 方法返回 false 時按照實現路線執行
異常處理
在SpringMVC中,無論請求控制器中是否存在異常,都會返回ModelAndView物件
Spring MVC 通過 HandlerExceptionResolver 處理程式的異常,包括 Handler 對映、資料繫結以及目標方法執行時發生的異常
DispatcherServlet 預設裝配的 HandlerExceptionResolver 有 DefaultHandlerExceptionResolver 解析器會自動將標準的Spring MVC異常解析為HTTP錯誤狀態碼
使用 <mvc:annotation-driven/> 配置會裝配Spring3.0後新增的異常解析器,實現更精細化處理。如果希望對所有異常進行統一處理或指定某一異常跳轉頁面,可以使用 SimpleMappingExceptionResolver,它將異常類名對映為檢視名,可實現跳轉到指定頁面,並報告異常.
配置異常解析器
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 為所有異常定義預設的處理頁面,exceptionMappings未定義的, value表示跳轉頁面,至於檔案路徑和字尾已經在 viewResolver 中指定--> <property name="defaultErrorView" value="error"></property> <!-- 定義異常處理頁面用來獲取異常資訊的變數名,被存放到 request 域中--> <property name="exceptionAttribute" value="exception"></property> <!-- 需要特殊處理的異常,全類名作為key,異常頁檔名作為值,可將不同的異常對映到不同的頁面上--> <property name="exceptionMappings"> <props> <prop key="java.lang.NullPointerException">nullPointer</prop> </props> </property> </bean>
<mvc:annotation-driven/>
是spring MVC為@Controllers分發請求所必須的,即啟用註解驅動,解決了@Controller註解使用的前提配置。
同時它還提供了:資料繫結支援,@NumberFormatannotation支援,@DateTimeFormat支援,@Valid支援,讀寫XML的支援(JAXB,讀寫JSON的支援(Jackson)。
它會自動為我們註冊了很多的Bean,最重要的就是RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
第一個是HandlerMapping的實現類,它會處理@RequestMapping 註解,並將其註冊到請求對映表中。
第二個是HandlerAdapter的實現類,它是處理請求的介面卡,說白了,就是確定呼叫哪個類的哪個方法,並且構造方法引數,返回值。
簡單的說,用什麼註解,就需要宣告對應的BeanPostProcessor。而Spring為我們提供了一種極為方便註冊這些BeanPostProcessor的方式,即使用各種標籤來隱式地向 Spring 容器註冊
Spring工作流程
相關類
HandlerMapping(請求處理器的對映物件):定義了一個所有請求和請求處理器物件之間的對映關係物件
HandlerExecutionChain(請求處理器執行鏈物件):定義了 當前請求處理器物件,和所有攔截器物件。
HandlerAdapter(請求處理器的介面卡物件):呼叫當前請求處理器的請求方法。
執行流程對應下圖理解
1)使用者向伺服器傳送請求,請求被SpringMVC 前端控制器 DispatcherServlet捕獲
2)DispatcherServlet對請求URL進行解析,得到請求資源識別符號(URI):判斷請求URI對應的對映
① 不存在:
再判斷是否配置了 mvc:default-servlet-handler:
如果沒配置,則控制檯報對映查詢不到,客戶端展示404錯誤
如果有配置,則執行目標資源(一般為靜態資源,如:JS,CSS,HTML)
② 存在:
執行下面流程
3)根據該URI,呼叫HandlerMapping獲得該Handler配置的所有相關的物件(包括Handler物件以及Handler物件對應的攔截器),最後以HandlerExecutionChain物件的形式返回;
4)DispatcherServlet 根據獲得的Handler,選擇一個合適的HandlerAdapter。
5)如果成功獲得HandlerAdapter後,此時將開始執行攔截器的preHandler(...)方法【正向】
6)提取Request中的模型資料,填充Handler入參,開始執行Handler(Controller)方法,處理請求。在填充Handler的入參過程中,根據你的配置,Spring將幫你做一些額外的工作:
① HttpMessageConveter: 將請求訊息(如Json、xml等資料)轉換成一個物件,將物件轉換為指定的響應資訊
② 資料轉換:對請求訊息進行資料轉換。如String轉換成Integer、Double等
③ 資料格式化:對請求訊息進行資料格式化。 如將字串轉換成格式化數字或格式化日期等
④ 資料驗證: 驗證資料的有效性(長度、格式等),驗證結果儲存到BindingResult或Error中
7)Handler執行完成後,向DispatcherServlet 返回一個ModelAndView物件;
8)此時將開始執行攔截器的postHandle(...)方法【逆向】
9)根據返回的ModelAndView(此時會判斷是否存在異常:如果存在異常,則執行HandlerExceptionResolver進行異常處理)選擇一個適合的ViewResolver(必須是已經註冊到Spring容器中的ViewResolver)返回給DispatcherServlet,根據Model和View,來渲染檢視
10)在返回給客戶端時需要執行攔截器的AfterCompletion方法【逆向】
11)將渲染結果返回給客戶端
Spring與SpringMVC
spring容器與springMVC容器物件的關係
springMVC容器物件,預設交個DispatcherServlet管理
spring容器物件,需要我們管理(交給Listener管理)
spring容器物件描述
Root WebApplicationContext: root of context hierarchy
springMVC容器物件描述
WebApplicationContext for namespace 'springDispatcherServlet-servlet':root of context hierarchy
spring容器物件是父,springMVC容器物件是子。子類可以直接呼叫父類方法。
SpringMVC 的 IOC 容器中的 bean 可以引用 Spring IOC 容器中的 bean.反之則不行.
在web應用下,獲取spring容器物件方式
ServletContext servletContext = httpSession.getServletContext(); ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
在web應用下,獲取springMVC容器物件可直接通過裝配屬性的方式獲取
@Autowired private XmlWebApplicationContext context;
配置檔案
若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器掃描元件的包有重合的部分, 就會導致有的 bean 會被建立 2 次,可通過一個包含一個排除的方式解