Spring Boot 攔截器、過濾器、監(jiān)聽器

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

GitHub源碼地址

1. 攔截器、過濾器、監(jiān)聽器區(qū)別

  • 攔截器(interceptor):依賴于web框架,基于Java的反射機(jī)制,屬于AOP的一種應(yīng)用。一個(gè)攔截器實(shí)例在一個(gè)controller生命周期內(nèi)可以多次調(diào)用。只能攔截Controller的請求。
  • 過濾器(Filter):依賴于Servlet容器,基于函數(shù)回掉,可以對幾乎所有請求過濾,一個(gè)過濾器實(shí)例只能在容器初使化調(diào)用一次。
  • 監(jiān)聽器(Listener):web監(jiān)聽器是Servlet中的特殊的類,用于監(jiān)聽web的特定事件,隨web應(yīng)用啟動(dòng)而啟動(dòng),只初始化一次。

2. 有什么用

  • 攔截器(interceptor):在一個(gè)請求進(jìn)行中的時(shí)候,你想干預(yù)它的進(jìn)展,甚至控制是否終止。這是攔截器做的事。
  • 過濾器(Filter):當(dāng)有一堆東西,只希望選擇符合的東西。定義這些要求的工具,就是過濾器。
  • 監(jiān)聽器(Listener):一個(gè)事件發(fā)生后,只希望獲取這些事個(gè)事件發(fā)生的細(xì)節(jié),而不去干預(yù)這個(gè)事件的執(zhí)行過程,這就用到監(jiān)聽器

3. 啟動(dòng)順序

監(jiān)聽器 >  過濾器 > 攔截器

4.SpringBoot中的具體實(shí)現(xiàn)

(1) 攔截器

  1. 攔截器常用有兩種方式實(shí)現(xiàn)
    • 實(shí)現(xiàn)HandlerInterceptor接口
    • 繼承HandlerInterceptorAdapter 抽象類
  2. 區(qū)別和聯(lián)系
  • HandlerInterceptorAdapter 實(shí)現(xiàn)AsyncHandlerInterceptor接口,AsyncHandlerInterceptor接口 繼承HandlerInterceptor接口.
  • AsyncHandlerInterceptor接口多了一個(gè)afterConcurrentHandlingStarted方法
  1. 具體方法
  • preHandle //請求過來之后首先走的方法 return true 繼續(xù)往下執(zhí)行
  • postHandle //請求之后返回之前
  • afterCompletion //處理完成之后
  • afterConcurrentHandlingStarted //如果返回一個(gè)current類型的變量,會啟用一個(gè)新的線程。執(zhí)行完preHandle方法之后立即會調(diào)用afterConcurrentHandlingStarted,然后新線程再以次執(zhí)行preHandle,postHandle,afterCompletion
  1. 代碼實(shí)現(xiàn)

【注】以下代碼基于springboot2.0

(1)攔截器

MyInterceptor1 繼承 HandlerInterceptorAdapter

MyInterceptor2 實(shí)現(xiàn) HandlerInterceptor接口

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 執(zhí)行:" + (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 執(zhí)行:" + (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)配置

@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) 運(yùn)行結(jié)果

  1. 請求/hello1

    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor2 執(zhí)行:516
    >>>>> MyInterceptor2 postHandle >>>>>>>>>>>>>>>>>>>>>
    MyInterceptor1 執(zhí)行:516
    >>>>> MyInterceptor1 postHandle >>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 afterCompletion >>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterCompletion >>>>>>>>>>>>>>>>
    

    執(zhí)行按preHandle > postHandle > afterCompletion

  2. 請求/hello2 或 /hello3

    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterConcurrentHandlingStarted >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 preHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 preHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor2 執(zhí)行:1
    >>>>> MyInterceptor2 postHandle >>>>>>>>>>>>>>>>>>>>>>
    MyInterceptor1 執(zhí)行:1
    >>>>> MyInterceptor1 postHandle >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor2 afterCompletion >>>>>>>>>>>>>>>>>>>>>>
    >>>>> MyInterceptor1 afterCompletion >>>>>>>>>>>>>>>>>>>>>>
    

    MyInterceptor1 執(zhí)行順序 preHandle > afterConcurrentHandlingStarted > preHandle > postHandle >afterCompletion

    MyInterceptor2 執(zhí)行順序 preHandle > preHandle > postHandle > afterCompletion

綜上.對于concurrent類型的返回值,spring會啟用一個(gè)新的線程來處理concurrent類型消息,在新的線程中會重新調(diào)用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 全局?jǐn)r截的配置(/*)和 Interceptor(/**)有所區(qū)別需要注意

(3) 監(jiān)聽器

(1) 監(jiān)聽器

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源碼地址

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

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