天天看點

Spring Boot中使用攔截器Spring Boot中使用攔截器

Spring Boot中使用攔截器

攔截器的原理很簡單,是 AOP 的一種實作,專門攔截對動态資源的背景請求,即攔截對控制層的請求。使用場景比較多的是判斷使用者是否有權限請求背景,更拔高一層的使用場景也有,比如攔截器可以結合 websocket 一起使用,用來攔截 websocket 請求,然後做相應的處理等等。攔截器不會攔截靜态資源,Spring Boot 的預設靜态目錄為 resources/static,該目錄下的靜态頁面、js、css、圖檔等等,不會被攔截(也要看如何實作,有些情況也會攔截,我在下文會指出)。

1. 攔截器的快速使用

使用攔截器很簡單,隻需要兩步即可:定義攔截器和配置攔截器。在配置攔截器中,Spring Boot 2.0 以後的版本和之前的版本有所不同,

1.1 定義攔截器

定義攔截器,隻需要實作

HandlerInterceptor

接口,

HandlerInterceptor

接口是所有自定義攔截器或者 Spring Boot 提供的攔截器的鼻祖,是以,首先來了解下該接口。該接口中有三個方法:

preHandle(……)

postHandle(……)

afterCompletion(……)

preHandle(……)

方法:該方法的執行時機是,當某個 url 已經比對到對應的 Controller 中的某個方法,且在這個方法執行之前。是以

preHandle(……)

方法可以決定是否将請求放行,這是通過傳回值來決定的,傳回 true 則放行,傳回 false 則不會向後執行。

postHandle(……)

方法:該方法的執行時機是,當某個 url 已經比對到對應的 Controller 中的某個方法,且在執行完了該方法,但是在 DispatcherServlet 視圖渲染之前。是以在這個方法中有個 ModelAndView 參數,可以在此做一些修改動作。

afterCompletion(……)

方法:顧名思義,該方法是在整個請求處理完成後(包括視圖渲染)執行,這時做一些資源的清理工作,這個方法隻有在

preHandle(……)

被成功執行後并且傳回 true 才會被執行。

了解了該接口,接下來自定義一個攔截器。

public class MyInterceptor implements HandlerInterceptor {
​
    private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
​
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        String methodName = method.getName();
        logger.info("====攔截到了方法:{},在該方法執行之前執行====", methodName);
        // 傳回true才會繼續執行,傳回false則取消目前請求
        return true;
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("執行完方法之後進執行(Controller方法調用之後),但是此時還沒進行視圖渲染");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("整個請求都處理完咯,DispatcherServlet也渲染了對應的視圖咯,此時我可以做一些清理的工作了");
    }
}
           

OK,到此為止,攔截器已經定義完成,接下來就是對該攔截器進行攔截配置。

1.2 配置攔截器

在 Spring Boot 2.0 之前,我們都是直接繼承 WebMvcConfigurerAdapter 類,然後重寫

addInterceptors

方法來實作攔截器的配置。但是在 Spring Boot 2.0 之後,該方法已經被廢棄了(當然,也可以繼續用),取而代之的是 WebMvcConfigurationSupport 方法,如下:

@Configuration
public class MyInterceptorConfig extends WebMvcConfigurationSupport {
​
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}
           

在該配置中重寫

addInterceptors

方法,将我們上面自定義的攔截器添加進去,

addPathPatterns

方法是添加要攔截的請求,這裡我們攔截所有的請求。這樣就配置好攔截器了,接下來寫一個 Controller 測試一下:

@Controller
@RequestMapping("/interceptor")
public class InterceptorController {
​
    @RequestMapping("/test")
    public String test() {
        return "hello";
    }
}
           

讓其跳轉到 hello.html 頁面,直接在 hello.html 中輸出

hello interceptor

即可。啟動項目,在浏覽器中輸入

localhost:8080/interceptor/test

看一下控制台的日志:

====攔截到了方法:test,在該方法執行之前執行====  
執行完方法之後進執行(Controller方法調用之後),但是此時還沒進行視圖渲染  
整個請求都處理完咯,DispatcherServlet也渲染了對應的視圖咯,此時我可以做一些清理的工作了
           

可以看出攔截器已經生效,并能看出其執行順序。

1.3 解決靜态資源被攔截問題

上文中已經介紹了攔截器的定義和配置,但是這樣是否就沒問題了呢?其實不然,如果使用上面這種配置的話,我們會發現一個缺陷,那就是靜态資源被攔截了。可以在 resources/static/ 目錄下放置一個圖檔資源或者 html 檔案,然後啟動項目直接通路,即可看到無法通路的現象。

也就是說,雖然 Spring Boot 2.0 廢棄了WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又會導緻預設的靜态資源被攔截,這就需要我們手動将靜态資源放開。

如何放開呢?除了在 MyInterceptorConfig 配置類中重寫

addInterceptors

方法外,還需要再重寫一個方法:

addResourceHandlers

,将靜态資源放開:

/**
 * 用來指定靜态資源不被攔截,否則繼承WebMvcConfigurationSupport這種方式會導緻靜态資源無法直接通路
 * @param registry
 */
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    super.addResourceHandlers(registry);
}
           

這樣配置好之後,重新開機項目,靜态資源也可以正常通路了。如果你是個善于學習或者研究的人,那肯定不會止步于此,沒錯,上面這種方式的确能解決靜态資源無法通路的問題,但是,還有更友善的方式來配置。

我們不繼承 WebMvcConfigurationSupport 類,直接實作 WebMvcConfigurer 接口,然後重寫

addInterceptors

方法,将自定義的攔截器添加進去即可,如下:

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 實作WebMvcConfigurer不會導緻靜态資源被攔截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}
           

這樣就非常友善了,實作 WebMvcConfigure 接口的話,不會攔截 Spring Boot 預設的靜态資源。

這兩種方式都可以,具體他們之間的細節,感興趣的讀者可以做進一步的研究,由于這兩種方式的不同,繼承 WebMvcConfigurationSupport 類的方式可以用在前後端分離的項目中,背景不需要通路靜态資源(就不需要放開靜态資源了);實作 WebMvcConfigure 接口的方式可以用在非前後端分離的項目中,因為需要讀取一些圖檔、css、js檔案等等。

繼續閱讀