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

開篇

在Web應用中,經常存在攔截所有或部分請求進行統一處理的應用場景,如權限校驗、參數校驗、性能監控等。 在SpringMVC框架中,咱們能夠經過過濾器或攔截器實現相關功能,spring-boot-starter-web模塊底層實際就是SpringMVC框架,那麼在SpringBoot項目中如何使用過濾器或攔截器呢?git

過濾器與攔截器的區別

項目 過濾器Filter 攔截器Interceptor 說明
規範定義 Servlet規範中定義,與SpringMVC框架無關。 SpringMVC提供組件之一。 過濾器不依賴於Spring MVC框架。
調用順序 在Spring DispatchServlet執行前 在Spring DispatchServlet中調用 故過濾器執行會在攔截器以前。
容器資源 沒法使用Spring容器資源,若是須要使用,能夠經過ApplicationContext上下文對象獲取 攔截器自己就是Spring的容器資源,能夠經過Ioc進行依賴注 入直接使用容器資源。

過濾器與攔截器的使用

建立項目

建立一個maven項目spring-boot-examples-intercept,添加一下依賴:web

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

添加業務接口

添加一個HelloController類,實現一個Restful的測試接口/hello,代碼以下:spring

@RestController
@Slf4j
public class HelloController {

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

添加過濾器

過濾器開發

  • 添加第一個過濾器FirstFilter類,實現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("[{}]執行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
        //執行下一個filter
        filterChain.doFilter(servletRequest, servletResponse);
        log.info("[{}]執行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
    }
}
  • 添加第二個過濾器SecondFilter類,實現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("[{}]執行{}方法:Before!", this.getClass().getSimpleName(), "doFilter");
        //執行下一個filter
        filterChain.doFilter(servletRequest, servletResponse);
        log.info("[{}]執行{}方法:After!", this.getClass().getSimpleName(), "doFilter");
    }
}

過濾器配置

配置過濾器有兩種方式,分別是經過配置類或者註解的方式,兩種方式均可以將過濾器配置到服務中,可是經過註解的方式我還沒發現指定過濾器順序的方法(經過@Order註解是無效的),因此若是須要指定過濾器執行順序的,建議使用方式一,不然使用方式二代碼更簡潔。後端

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

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

    /**
     * 註冊第二個過濾器
     * @return
     */
    @Bean
    public FilterRegistrationBean secondFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new SecondFilter());
        //可不設置,默認過濾路徑即爲:/*
        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);
    }
}

添加攔截器

開發攔截器

  • 添加第一個攔截器FirstInterceptor類,實現HandlerInterceptor接口,代碼以下:
@Slf4j
public class FirstInterceptor implements HandlerInterceptor {

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

    /**
     * controller方法調用後視圖渲染前執行。
     *
     * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "postHandle");
    }

    /**
     * controller方法調用且視圖渲染完成後執行
     *
     * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
    }
  • 添加第二個攔截器SecondInterceptor類,實現HandlerInterceptor接口,代碼以下:
@Slf4j
public class SecondInterceptor implements HandlerInterceptor {

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

    /**
     * controller方法調用後視圖渲染前執行。
     *
     * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "postHandle");
    }

    /**
     * controller方法調用且視圖渲染完成後執行
     *
     * @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("[{}]執行{}方法!", this.getClass().getSimpleName(), "afterCompletion");
    }

配置攔截器

添加一個Spring Boot配置類,實現WebMvcConfigurer接口以覆蓋容器默認配置,代碼以下:springboot

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

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

}

啓動測試

啓動服務,訪問http://localhost:8120/hello接口,後臺輸入日誌以下圖,會發現Filter執行會在Interceptor以前,也驗證上面表格中的說法。 image.pngapp

示例代碼

碼雲:https://gitee.com/centy/spring-boot-examples/tree/master/spring-boot-examples-intercept框架

尾巴

過濾器依賴於Servlet容器,而Interceptor則爲SpringMVC的一部分。過濾器可以攔截全部請求,而Interceptor只能攔截Controller的請求,因此從覆蓋範圍來看,Filter應用更廣一些。可是在Spring逐漸一統Java框架、先後端分離越演越烈,實際上大部分的應用場景,攔截器均可以知足了。前後端分離

相關文章
相關標籤/搜索