springboot學習13——springmvc攔截器解析

八、攔截器

當請求來到DispatcherServlet時,它會根據HandlerMapping的機制找到處理器,這樣就會返回一個HandlerExecutionChain對象,這個對象包含處理器和攔截器。
這裏的攔截器會對處理器進行攔截,這樣通過攔截器就可以增強處理器的功能。

1.攔截器的設計
首先所有的攔截器都需要實現HandlerInterceptor接口。

HandlerInterceptor源碼

package org.springframework.web.servlet;

/**** imports ****/
public interface HandlerInterceptor {

    // 處理器執行前方法
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
        Object handler) throws Exception {
        return true;
    }

    // 處理器處理後方法
    default void postHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    // 處理器完成後方法
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
        Object handler, @Nullable Exception ex) throws Exception {
    }

}

在這裏插入圖片描述

攔截器執行過程:
•執行preHandle方法,該方法會返回一個布爾值。如果爲false,則結束所有流程;如果爲true,則執行下一步。
•執行處理器邏輯,它包含控制器的功能。
•執行postHandle方法。
•執行視圖解析和視圖渲染。
•執行afterCompletion方法。
因爲這個接口是Java 8的接口,所以3個方法都被聲明爲default,並且提供了空實現。當我們需要自己定義方法的時候,只需要實現HandlerInterceptor,覆蓋其對應的方法即可。

2.開發攔截器
自定義簡單攔截器

/******** 攔截器1********/ 
package com.springboot.chapter10.interceptor;
/**** imports ****/
public class MulitiInterceptor1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】處理器前方法");
        // 返回true,不會攔截後續的處理
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】處理器後方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】處理器完成方法");
    }
}

這裏的代碼實現了HandlerInterceptor,然後按照自己的需要重寫了3個具體的攔截器方法。
有了這個攔截器,Spring MVC並不會發現它,它還需要進行註冊才能夠攔截處理器,爲此需要在配置文件中實現WebMvcConfigurer接口,最後覆蓋其addInterceptors方法進行註冊攔截器。

註冊攔截器

package com.springboot.chapter10.main;
/**** imports ****/
// 聲明配置類
@Configuration
// 定製掃描路徑
@SpringBootApplication(scanBasePackages = "com.springboot.chapter10")
/****其他註解****/
public class Chapter10Application implements WebMvcConfigurer {
    public static void main(String[] args) {
        SpringApplication.run(Chpter10Application.class, args);
    }
    ......
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 註冊攔截器到Spring MVC機制,然後它會返回一個攔截器註冊
        InterceptorRegistration ir = registry.addInterceptor(new Interceptor1());
        // 指定攔截匹配模式,限制攔截器攔截請求
        ir.addPathPatterns("/interceptor/*");
}
}

這裏通過實現WebMvcConfigurer接口,重寫其中的addInterceptors方法,進而加入自定義攔截器——Interceptor1,然後指定其攔截的模式,所以它只會攔截與正則式“/interceptor/*”匹配的請求。

攔截控制器

package com.springboot.chapter10.controller;
/**** imports ****/
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {
    @GetMapping("/start")
    public String start() {
        System.out.println("執行處理器邏輯");
        return "/welcome";
    }
}

這裏的控制器的start方法只是打開一個歡迎頁面,十分簡單,同時它定義了攔截“/interceptor/start”,而這個請求顯然會被所創建的攔截器所攔截,所以只需要請求這個方法,請求就會被我們的攔截器攔截。
爲了更好地測試這個攔截器,我們在歡迎頁面也打印一下後臺的信息,這個頁面如代碼清單10-38所示。

測試:http://localhost:8080/interceptor/start
後臺打印的日誌:

【MulitiInterceptor1】處理器前方法
執行處理器邏輯
【MulitiInterceptor1】處理器後方法
【MulitiInterceptor1】處理器完成方法

顯然處理器被攔截器攔截了,這裏需要注意的是攔截器方法的執行順序。有興趣的讀者可以把攔截器的preHandle方法返回修改爲false,或者讓控制器拋出異常,然後重新測試,從而進一步掌握整個攔截器的流程。

3.多個攔截器的順序
定義多個攔截器
攔截器2 ,攔截器3除類名外,內容同攔截器1。

註冊多個攔截器

@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 註冊攔截器到Spring MVC機制中
    InterceptorRegistration ir 
        = registry.addInterceptor(new MulitiInterceptor1());
    // 指定攔截匹配模式
    ir.addPathPatterns("/interceptor/*");
    // 註冊攔截器到Spring MVC機制中
    InterceptorRegistration ir2 = registry.addInterceptor(new MulitiInterceptor2());
    // 指定攔截匹配模式
    ir2.addPathPatterns("/interceptor/*");
    // 註冊攔截器到Spring MVC機制中
    InterceptorRegistration ir3 = registry.addInterceptor(new MulitiInterceptor3());
    // 指定攔截匹配模式
    ir3.addPathPatterns("/interceptor/*");
}

這樣這些攔截器都會攔截與"/interceptor/*"匹配的請求。這裏使用瀏覽器再次請求代碼清單10-37中的start方法,於是可以看到如下的日誌打印出來:

【MulitiInterceptor1】處理器前方法
【MulitiInterceptor2】處理器前方法
【MulitiInterceptor3】處理器前方法
執行處理器邏輯
【MulitiInterceptor3】處理器後方法
【MulitiInterceptor2】處理器後方法
【MulitiInterceptor1】處理器後方法
【MulitiInterceptor3】處理器完成方法
【MulitiInterceptor2】處理器完成方法
【MulitiInterceptor1】處理器完成方法

這個結果是責任鏈模式的規則,對於處理器前方法採用先註冊先執行,而處理器後方法和完成方法則是先註冊後執行的規則。

只是上述僅測試了處理器前(preHandle)方法返回爲true的場景,在某些時候還可能返回爲false,這個時候又如何呢?爲此,可以將MulitiInterceptor2的preHandle方法修改返回爲false,
然後再進行測試,其日誌如下:
【MulitiInterceptor1】處理器前方法
【MulitiInterceptor2】處理器前方法
【MulitiInterceptor1】處理器完成方法
從上面的日誌可以看出,處理器前(preHandle)方法會執行,但是一旦返回false,則後續的攔截器、處理器和所有攔截器的處理器後(postHandle)方法都不會被執行。
完成方法afterCompletion則不一樣,它只會執行返回true的攔截器的完成方法,而且順序是先註冊後執行。

九、Spring MVC拾遺

@ResponseBody轉換爲JSON的祕密

一直以來,當想把某個控制器的返回轉變爲JSON數據集時,只需要在方法上標註@ResponseBody註解即可,那麼Spring MVC是如何做到的呢?

在進入控制器方法前,當遇到標註的@ResponseBody後,處理器就會記錄這個方法的響應類型爲JSON數據集。當執行完控制器返回後,處理器會啓用結果解析器(ResultResolver)去解析這個結果,
它會去輪詢註冊給Spring MVC的HttpMessageConverter接口的實現類。
因爲MappingJackson2HttpMessageConverter這個實現類已經被Spring MVC所註冊,加上Spring MVC將控制器的結果類型標明爲JSON,所以就匹配上了,於是通過它就在處理器內部把結果轉換爲了JSON。
當然有時候會輪詢不到匹配的HttpMessageConverter,那麼它就會交由Spring MVC後續流程去處理。
如果控制器返回結果被MappingJackson2HttpMessageConverter進行了轉換,那麼後續的模型和視圖(ModelAndView)就返回null,這樣視圖解析器和視圖渲染將不再被執行,其流程如下:

@ResponseBody註解轉換爲JSON流程圖在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章