SpringBoot入坑指南之六:使用過濾器或攔截器

開篇

在Web應用中,常常存在攔截全部或部分請求進行統(tǒng)一處理的應用場景,如權(quán)限校驗、參數(shù)校驗、性能監(jiān)控等。
在SpringMVC框架中,我們可以通過過濾器或攔截器實現(xiàn)相關(guān)功能,spring-boot-starter-web模塊底層實際就是SpringMVC框架,那么在SpringBoot項目中如何使用過濾器或攔截器呢?

過濾器與攔截器的區(qū)別

項目 過濾器Filter 攔截器Interceptor 說明
規(guī)范定義 Servlet規(guī)范中定義,與SpringMVC框架無關(guān)。 SpringMVC提供組件之一。 過濾器不依賴于Spring MVC框架。
調(diào)用順序 在Spring DispatchServlet執(zhí)行前 在Spring DispatchServlet中調(diào)用 故過濾器執(zhí)行會在攔截器之前。
容器資源 無法使用Spring容器資源,如果需要使用,可以通過ApplicationContext上下文對象獲取 攔截器本身就是Spring的容器資源,可以通過Ioc進行依賴注入直接使用容器資源。 單從Restful的接口請求來說,過濾器能做的攔截器都能做,從Bean管理角度,都建議使用攔截器實現(xiàn)。

過濾器與攔截器的使用

創(chuàng)建項目

創(chuàng)建一個maven項目spring-boot-examples-intercept,添加一下依賴:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

添加業(yè)務接口

添加一個HelloController類,實現(xiàn)一個Restful的測試接口/hello,代碼如下:

@RestController
@Slf4j
public class HelloController {

    /**
     * 測試請求方法
     *
     * @return
     */
    @GetMapping("/hello")
    public String hello() {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "hello");
        return "Hello!";
    }
}

添加過濾器

過濾器開發(fā)

  • 添加第一個過濾器FirstFilter類,實現(xiàn)Filter接口,代碼如下:
@Slf4j
public class FirstFilter implements Filter {

    /**
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("[{}]執(zhí)行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
        //執(zhí)行下一個filter
        filterChain.doFilter(servletRequest, servletResponse);
        log.info("[{}]執(zhí)行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
    }
}
  • 添加第二個過濾器SecondFilter類,實現(xiàn)Filter接口,代碼如下:
@Slf4j
public class SecondFilter implements Filter {

    /**
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("[{}]執(zhí)行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
        //執(zhí)行下一個filter
        filterChain.doFilter(servletRequest, servletResponse);
        log.info("[{}]執(zhí)行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
    }
}

過濾器配置

配置過濾器有兩種方式,分別是通過配置類或者注解的方式,兩種方式都可以將過濾器配置到服務中,但是通過注解的方式我還沒發(fā)現(xiàn)指定過濾器順序的方法(通過@Order注解是無效的),所以如果需要指定過濾器執(zhí)行順序的,建議使用方式一,否則使用方式二代碼更簡潔。

  • 方式一:通過配置類配置過濾器。
    添加一個Spring Boot的配置類FilterConfig,里面注冊兩個FilterRegistrationBean類型的Bean,代碼如下:
@Configuration
public class FilterConfig {

    /**
     * 注冊第一個過濾器
     * @return
     */
    @Bean
    public FilterRegistrationBean firstFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new FirstFilter());
        //可不設(shè)置,默認過濾路徑即為:/*
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        return registrationBean;
    }

    /**
     * 注冊第二個過濾器
     * @return
     */
    @Bean
    public FilterRegistrationBean secondFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new SecondFilter());
        //可不設(shè)置,默認過濾路徑即為:/*
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(2);
        return registrationBean;
    }
}
  • 方式二:通過注解配置過濾器
    在FirstFilter和SecondFilter類上分別添加注解配置@WebFilter(urlPatterns = "/*"),由于@WebFilter并不是Spring提供的注解,所以還需要在項目的啟動類中添加注解配置@ServletComponentScan,告訴Spring掃描路徑,如下:
@SpringBootApplication
@ServletComponentScan(basePackages = "org.cent.springboot.example.intercept.filter")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

添加攔截器

開發(fā)攔截器

  • 添加第一個攔截器FirstInterceptor類,實現(xiàn)HandlerInterceptor接口,代碼如下:
@Slf4j
public class FirstInterceptor implements HandlerInterceptor {

    /**
     * controller方法調(diào)用前調(diào)用。
     *
     * @param request
     * @param response
     * @param handler
     * @return 往下執(zhí)行則返回true,否則返回false
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName());
        return true;
    }

    /**
     * controller方法調(diào)用后視圖渲染前執(zhí)行。
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "postHandle");
    }

    /**
     * controller方法調(diào)用且視圖渲染完成后執(zhí)行
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
    }
  • 添加第二個攔截器SecondInterceptor類,實現(xiàn)HandlerInterceptor接口,代碼如下:
@Slf4j
public class SecondInterceptor implements HandlerInterceptor {

    /**
     * controller方法調(diào)用前調(diào)用。
     *
     * @param request
     * @param response
     * @param handler
     * @return 往下執(zhí)行則返回true,否則返回false
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "preHandle");
        return true;
    }

    /**
     * controller方法調(diào)用后視圖渲染前執(zhí)行。
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "postHandle");
    }

    /**
     * controller方法調(diào)用且視圖渲染完成后執(zhí)行
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("[{}]執(zhí)行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
    }

配置攔截器

添加一個Spring Boot配置類,實現(xiàn)WebMvcConfigurer接口以覆蓋容器默認配置,代碼如下:

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    /**
     * 重寫添加攔截器方法
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new FirstInterceptor())
                .addPathPatterns("/**")
                .order(1);//指定執(zhí)行順序,數(shù)值越小越優(yōu)先
        registry.addInterceptor(new SecondInterceptor())
                .addPathPatterns("/hello")
                .order(2);//指定執(zhí)行順序,數(shù)值越小越優(yōu)先
    }

}

啟動測試

啟動服務,訪問http://localhost:8120/hello接口,后臺輸入日志如下圖,會發(fā)現(xiàn)Filter執(zhí)行會在Interceptor之前,也驗證上面表格中的說法。

image.png

示例代碼

碼云:https://gitee.com/centy/spring-boot-examples/tree/master/spring-boot-examples-intercept

尾巴

過濾器依賴于Servlet容器,而Interceptor則為SpringMVC的一部分。過濾器能夠攔截所有請求,而Interceptor只能攔截Controller的請求,所以從覆蓋范圍來看,F(xiàn)ilter應用更廣一些。但是在Spring逐漸一統(tǒng)Java框架、前后端分離越演越烈,實際上大部分的應用場景,攔截器都可以滿足了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容