SpringBoot 攔截器、過濾器、監聽器

  在工做中使用Web框架,老是避免不了與這些概念打交道,作一下總結,一口氣說完攔截器、過濾器、監聽器。java

GitHub源碼地址git

1. 攔截器、過濾器、監聽器區別

  • 攔截器(interceptor):依賴於web框架,基於Java的反射機制,屬於AOP的一種應用。一個攔截器實例在一個controller生命週期內能夠屢次調用。只能攔截Controller的請求。
  • 過濾器(Filter):依賴於Servlet容器,基於函數回掉,能夠對幾乎全部請求過濾,一個過濾器實例只能在容器初使化調用一次。
  • 監聽器(Listener):web監聽器是Servlet中的特殊的類,用於監聽web的特定事件,隨web應用啓動而啓動,只初始化一次。

2. 有什麼用

  • 攔截器(interceptor):在一個請求進行中的時候,你想幹預它的進展,甚至控制是否終止。這是攔截器作的事。
  • 過濾器(Filter):當有一堆東西,只但願選擇符合的東西。定義這些要求的工具,就是過濾器。
  • 監聽器(Listener):一個事件發生後,只但願獲取這些事個事件發生的細節,而不去幹預這個事件的執行過程,這就用到監聽器

3. 啓動順序

監聽器 >  過濾器 > 攔截器
複製代碼

4.SpringBoot中的具體實現

(1) 攔截器

  1. 攔截器經常使用有兩種方式實現
    • 實現HandlerInterceptor接口
    • 繼承HandlerInterceptorAdapter 抽象類
  2. 區別和聯繫
  • HandlerInterceptorAdapter 實現AsyncHandlerInterceptor接口,AsyncHandlerInterceptor接口 繼承HandlerInterceptor接口.
  • AsyncHandlerInterceptor接口多了一個afterConcurrentHandlingStarted方法
  1. 具體方法
  • preHandle //請求過來以後首先走的方法 return true 繼續往下執行
  • postHandle //請求以後返回以前
  • afterCompletion //處理完成以後
  • afterConcurrentHandlingStarted //若是返回一個current類型的變量,會啓用一個新的線程。執行完preHandle方法以後當即會調用afterConcurrentHandlingStarted,而後新線程再以次執行preHandle,postHandle,afterCompletion
  1. 代碼實現

【注】如下代碼基於springboot2.0github

(1)攔截器web

MyInterceptor1 繼承 HandlerInterceptorAdapterspring

MyInterceptor2 實現 HandlerInterceptor接口springboot

public class MyInterceptor1 extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());
        System.out.println(">>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (long) request.getAttribute("startTime");
        System.out.println("MyInterceptor1 執行:" + (System.currentTimeMillis() - startTime));
        System.out.println(">>>>> MyInterceptor1 postHandle >>>>>>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        request.removeAttribute("startTime");
        System.out.println(">>>>> MyInterceptor1 afterCompletion >>>>>>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
        System.out.println(">>>>> MyInterceptor1 afterConcurrentHandlingStarted >>>>>>>>>>>>>>>>>>>>>>");
    }
}
複製代碼
public class MyInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("startTime", System.currentTimeMillis());
        System.out.println(">>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (long) request.getAttribute("startTime");
        System.out.println("MyInterceptor2 執行:" + (System.currentTimeMillis() - startTime));
        System.out.println(">>>>> MyInterceptor2 postHandle >>>>>>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        request.removeAttribute("startTime");
        System.out.println(">>>>> MyInterceptor2 afterCompletion >>>>>>>>>>>>>>>>>>>>>>");
    }
}
複製代碼

(2)配置app

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
    }
}
複製代碼

(3)請求框架

@RestController
@SpringBootApplication
public class SpringbootInterceptorApplication {

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


    @GetMapping(value = "/hello1")
    public ResponseEntity<String> hello() throws InterruptedException {
        Thread.sleep(500);
        return ResponseEntity.ok("HelloWorld");
    }

    @GetMapping(value = "/hello2")
    public StreamingResponseBody hello2() throws InterruptedException {
        Thread.sleep(500);
        return (OutputStream outputStream) -> {
            outputStream.write("success".getBytes());
            outputStream.flush();
            outputStream.close();
        };
    }

    @GetMapping(value = "/hello3")
    public Future<String> hello3() throws InterruptedException {
        Thread.sleep(500);
        return new AsyncResult<>("Hello");
    }
}
複製代碼

(4) 運行結果ide

  1. 請求/hello1函數

    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor2 執行:516
    >>>>> MyInterceptor2 postHandle >>>>>>>>>>>>>>>>>>>>>
    MyInterceptor1 執行:516
    >>>>> MyInterceptor1 postHandle >>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 afterCompletion >>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterCompletion >>>>>>>>>>>>>>>>
    複製代碼

    執行按preHandle > postHandle > afterCompletion

  2. 請求/hello2 或 /hello3

    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterConcurrentHandlingStarted >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor2 執行:1
    >>>>> MyInterceptor2 postHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor1 執行:1
    >>>>> MyInterceptor1 postHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 afterCompletion >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterCompletion >>>>>>>>>>>>>>>>>>>>>>
    複製代碼

    MyInterceptor1 執行順序 preHandle > afterConcurrentHandlingStarted > preHandle > postHandle >afterCompletion

    MyInterceptor2 執行順序 preHandle > preHandle > postHandle > afterCompletion

綜上.對於concurrent類型的返回值,spring會啓用一個新的線程來處理concurrent類型消息,在新的線程中會從新調用preHandle方法。

(2) 過濾器

(1) 過濾器

public class MyFilter1 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(filterConfig.getInitParameter("initParam"));
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter1 >>>>>>>>>>>");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}
複製代碼

(2) 配置

  • 第一種方式
@Bean
public FilterRegistrationBean<MyFilter1> filterRegistrationBean() {
    FilterRegistrationBean<MyFilter1> filterRegistrationBean = new FilterRegistrationBean<>();
    filterRegistrationBean.addUrlPatterns("/*");//過濾全部
    filterRegistrationBean.setFilter(new MyFilter1());
    filterRegistrationBean.setOrder(1);
    filterRegistrationBean.addInitParameter("initParam", "initOk");
    return filterRegistrationBean;
}
複製代碼
  • 第二種方式
@Bean
public MyFilter1 myFilter() {
    return new MyFilter1();
}
複製代碼
  • 第三種方式
@WebFilter("/test/*")
public class MyFilter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter2");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("DoFilter 2");
    }
}
複製代碼

經過@WebFilter("/test/*")註解,首先須要@ServletComponentScan("com.jiuxian")

Filter 全局攔截的配置(/*)和 Interceptor(/**)有所區別須要注意

(3) 監聽器

(1) 監聽器

public class MyListener1 implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("MyListener1 ... ");
    }
}
複製代碼

(2) 配置方式和Filter相似

  • 第一種方式
@Bean
public ServletListenerRegistrationBean<MyListener1> registrationBean() {
    ServletListenerRegistrationBean<MyListener1> servletListenerRegistrationBean
            = new ServletListenerRegistrationBean<>();
    servletListenerRegistrationBean.setListener(new MyListener1());
    return servletListenerRegistrationBean;
}
複製代碼
  • 第二種方式
@Bean
public MyListener1 myListener1() {
    return new MyListener1();
}
複製代碼
  • 第三種方式
@WebListener
public class MyListener2 implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("MyListener2");
    }
}
複製代碼

使用@WebListener註解,首先須要@ServletComponentScan("com.jiuxian")

【注】以上代碼基於springboot2.0

GitHub源碼地址

相關文章
相關標籤/搜索