天天看點

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()都會被執行。

Java架構之SpringMVC 05-攔截器-異常映射-Spring工作流程

  當兩個攔截器的 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)将渲染結果傳回給用戶端

Java架構之SpringMVC 05-攔截器-異常映射-Spring工作流程

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 次,可通過一個包含一個排除的方式解決