日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

过滤器、拦截器、监听器的区别与使用

發(fā)布時間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 过滤器、拦截器、监听器的区别与使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

  • 過濾器 (Filter)
    過濾器的配置比較簡單,直接實現Filter 接口即可,也可以通過@WebFilter注解實現對特定URL攔截,看到Filter 接口中定義了三個方法。
    • init() :該方法在容器啟動初始化過濾器時被調用,它在 Filter 的整個生命周期只會被調用一次。注意:這個方法必須執(zhí)行成功,否則過濾器會不起作用。
    • doFilter() :容器中的每一次請求都會調用該方法, FilterChain 用來調用下一個過濾器 Filter。
    • destroy(): 當容器銷毀過濾器實例時調用該方法,一般在方法中銷毀或關閉資源,在過濾器 Filter 的整個生命周期也只會被調用一次
  • 攔截器 (Interceptor)
    攔截器它是鏈式調用,一個應用中可以同時存在多個攔截器Interceptor, 一個請求也可以觸發(fā)多個攔截器 ,而每個攔截器的調用會依據它的聲明順序依次執(zhí)行。首先編寫一個簡單的攔截器處理類,請求的攔截是通過HandlerInterceptor 來實現,看到HandlerInterceptor 接口中也定義了三個方法。
    • preHandle() :這個方法將在請求處理之前進行調用。注意:如果該方法的返回值為false,將視為當前請求結束,不僅自身的攔截器會失效,還會導致其他的攔截器也不再執(zhí)行。
    • postHandle():只有在 preHandle()方法返回值為true 時才會執(zhí)行。會在Controller 中的方法調用之后,DispatcherServlet 返回渲染視圖之前被調用。有意思的是:postHandle() 方法被調用的順序跟 preHandle() 是相反的,先聲明的攔截器 preHandle() 方法先執(zhí)行,而postHandle()方法反而會后執(zhí)行。
    • afterCompletion():只有在 preHandle()方法返回值為true 時才會執(zhí)行。在整個請求結束之后, DispatcherServlet 渲染了對應的視圖之后執(zhí)行。
  • 過濾器和攔截器觸發(fā)時機不一樣,過濾器是在請求進入容器后,但請求進入servlet之前進行預處理的。請求結束返回也是,是在servlet處理完后,返回給前端之前。
    如下圖:
  • 攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,因為攔截器是spring提供并管理的,spring的功能可以被攔截器使用,在攔截器里注入一個service,可以調用業(yè)務邏輯。而過濾器是JavaEE標準,只需依賴servlet api ,不需要依賴spring。
  • 過濾器攔截器運行先后步驟:

    其中第2步,SpringMVC的機制是由DispaterServlet來分發(fā)請求給不同的Controller,其實這一步是在Servlet的service()方法中執(zhí)行的

  • 過濾器的實現基于回調函數。而攔截器(代理模式)的實現基于反射,代理分靜態(tài)代理和動態(tài)代理,動態(tài)代理是攔截器的簡單實現。
  • 攔截器加載的時間點在springcontext之前。
  • 過濾器幾乎可以對所有進入容器的請求起作用,而攔截器只會對Controller中請求或訪問static目錄下的資源請求起作用。
  • 過濾器 和 攔截器 均體現了AOP的編程思想,都可以實現諸如日志記錄、登錄鑒權等功能
  • 二、何時使用攔截器?何時使用過濾器?何時使用監(jiān)聽器

  • 如果是非spring項目,那么攔截器不能用,只能使用過濾器。
  • 如果是處理controller前后,既可以使用攔截器也可以使用過濾器。
  • 如果是處理dispaterServlet前后,只能使用過濾器。
  • 監(jiān)聽 Servlet 上下文用來初始化一些數據、監(jiān)聽 HTTP Session 用來獲取當前在線的人數、監(jiān)聽客戶端請求的 ServletRequest 對象來獲取用戶的訪問信息等等,使用監(jiān)聽器。
    • 具體使用場景
  • 過濾器:可以對請求的URL進行過濾, 對敏感詞過濾,
  • 攔截器:性能分析, 權限檢查, 日志記錄
  • 靈活性上說攔截器功能更強大些,Filter能做的事情,他都能做,而且可以在請求前,請求后執(zhí)行,比較靈活。Filter主要是針對URL地址做一個編碼的事情、過濾掉沒用的參數、安全校驗(比較泛的,比如登錄不登錄之類),太細的話,還是建議用interceptor。不過還是根據不同情況選擇合適的。

    三、spring boot如何使用過濾器、攔截器

  • Spring boot過濾器的使用(兩種方式)
  • 使用spring boot提供的FilterRegistrationBean注冊Filter
  • 使用原生servlet注解定義Filter
  • 兩種方式的本質都是一樣的,都是去FilterRegistrationBean注冊自定義Filter

    • 方式一:

    ①、先定義Filter:

    import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 處理request 或responseSystem.out.println("filter1");// 調用filter鏈中的下一個filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {} }

    ②、注冊自定義Filter

    @Configuration public class FilterConfig {@Beanpublic FilterRegistrationBean registrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;} }
    • 方式二:
    // 注入spring容器 @Component // 定義filterName 和過濾的url @WebFilter(filterName = "my2Filter" ,urlPatterns = "/*") public class My2Filter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter2");}@Overridepublic void destroy() {} }
  • Spring boot攔截器的使用:
  • ①、定義攔截器:

    public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {System.out.println("afterCompletion");} }

    ②、配置攔截器:

    @Configuration public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());} }

    ③Controller演示:

    @RestController public class UController {@GetMapping("/home")public String home(){System.out.println("home");return "myhome";} }

    四、過濾器攔截器完整例子

    @Configuration public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());registry.addInterceptor(new MyInterceptor2());} } @Order(1) @Component @WebFilter(filterName = "myFilter",urlPatterns = "/*") public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter1-----過濾器的init()方法,隨著容器的啟動進行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 處理request 或responseSystem.out.println("filter1");// 調用filter鏈中的下一個filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter1--------過濾器的destroy()方法,隨著容器的關閉而進行");} } @Order(2) @Component @WebFilter(filterName = "myFilter2",urlPatterns = "/*") public class MyFilter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter2------過濾器的init()方法,隨著容器的啟動進行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 處理request 或responseSystem.out.println("filter2");// 調用filter鏈中的下一個filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter2-----過濾器的destroy()方法,隨著容器的關閉而進行");} } @Order(3) @Component @WebFilter(filterName = "myFilter3",urlPatterns = "/*") public class MyFilter3 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter3------過濾器的init()方法,隨著容器的啟動進行了初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// do something 處理request 或responseSystem.out.println("filter3");// 調用filter鏈中的下一個filterfilterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {System.out.println("MyFilter3-------過濾器的destroy()方法,隨著容器的關閉而進行");} } public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {System.out.println("afterCompletion");} } @Controller public class MyController {@ResponseBody@RequestMapping("myTest")public String test(){System.out.println("controller.method invoked");return "ok";} }

    order定義filter的優(yōu)先級,值越小優(yōu)先級越高,因此運行順序為

    filter3—doFilter—>filter2—doFilter—>filter1—doFilter—>控制器方法—>返回到filter1—>返回到filter2—>返回到filter3

    注意:@Order或者接口Ordered的作用是定義Spring IOC容器中Bean的執(zhí)行順序的優(yōu)先級,而不是定義Bean的加載順序,Bean的加載順序不受@Order或Ordered接口的影響。

    可以看到只有執(zhí)行的時候,才會按filter1,filter2,filter3的順序執(zhí)行。同時我們注意到:先聲明的攔截器 preHandle() 方法先執(zhí)行,而postHandle()方法反而會后執(zhí)行。也即是:postHandle() 方法被調用的順序跟 preHandle() 居然是相反的。如果實際開發(fā)中嚴格要求執(zhí)行順序,那就需要特別注意這一點。那為什么會這樣呢? 得到答案就只能看源碼了,我們要知道controller 中所有的請求都要經過核心組件DispatcherServlet路由,都會執(zhí)行它的 doDispatch() 方法,而攔截器postHandle()、preHandle()方法便是在其中調用的。

    • doDispatch方法
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {...........try {// 獲取可以執(zhí)行當前Handler的適配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 注意: 執(zhí)行Interceptor中PreHandle()方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 注意:執(zhí)行Handle【包括我們的業(yè)務邏輯,當拋出異常時會被Try、catch到】mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 注意:執(zhí)行Interceptor中PostHandle 方法【拋出異常時無法執(zhí)行】mappedHandler.applyPostHandle(processedRequest, response, mv);}}...........}

    看看兩個方法applyPreHandle()、applyPostHandle()具體是如何被調用的,就明白為什么postHandle()、preHandle() 執(zhí)行順序是相反的了。

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = this.getInterceptors();if(!ObjectUtils.isEmpty(interceptors)) {for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {HandlerInterceptor interceptor = interceptors[i];if(!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}}return true;} void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = this.getInterceptors();if(!ObjectUtils.isEmpty(interceptors)) {for(int i = interceptors.length - 1; i >= 0; --i) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}

    發(fā)現兩個方法中在調用攔截器數組 HandlerInterceptor[] 時,循環(huán)的順序竟然是相反的。。。,導致postHandle()、preHandle() 方法執(zhí)行的順序相反。

    五、監(jiān)聽器

    Java Web開發(fā)中的監(jiān)聽器(listener)就是由application、session、request三個對象創(chuàng)建、銷毀或者往其中添加、修改、刪除屬性時自動執(zhí)行代碼的功能組件,如下所示:

    • ServletContextListener:對Servlet上下文的創(chuàng)建和銷毀進行監(jiān)聽。
    • ServletContextAttributeListener:監(jiān)聽Servlet上下文屬性的添加、刪除和修改。
    • HttpSessionListener:對Session的創(chuàng)建和銷毀進行監(jiān)聽。
    • HttpSessionAttributeListener:對Session對象中屬性的添加、刪除和修改進行監(jiān)聽。
    • HttpSessionBindingListener:監(jiān)聽Http會話中對象的綁定信息。
    • HttpSessionActivationListener:監(jiān)聽器監(jiān)聽Http會話的情況。
    • ServletRequestListener:對請求對象的初始化和銷毀進行監(jiān)聽。
    • ServletRequestAttributeListener:對請求對象屬性的添加、刪除和修改進行監(jiān)聽。

  • 統(tǒng)計網站最多在線人數監(jiān)聽器的例子
  • /*** 上下文監(jiān)聽器,在服務器啟動時初始化onLineCount和maxOnLineCount兩個變量,* 并將其置于服務器上下文(ServletContext)中,其初始值都是0。*/ @WebListener public class InitListener implements ServletContextListener {public void contextDestroyed(ServletContextEvent evt) {}public void contextInitialized(ServletContextEvent evt) {evt.getServletContext().setAttribute("onLineCount", 0);evt.getServletContext().setAttribute("maxOnLineCount", 0);} } /*** 會話監(jiān)聽器,在用戶會話創(chuàng)建和銷毀的時候根據情況修改onLineCount和maxOnLineCount的值。*/ @WebListener public class MaxCountListener implements HttpSessionListener {public void sessionCreated(HttpSessionEvent event) {ServletContext ctx = event.getSession().getServletContext();int count = Integer.parseInt(ctx.getAttribute("onLineCount").toString());count++;ctx.setAttribute("onLineCount", count);int maxOnLineCount = Integer.parseInt(ctx.getAttribute("maxOnLineCount").toString());if (count > maxOnLineCount) {ctx.setAttribute("maxOnLineCount", count);DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");ctx.setAttribute("date", df.format(new Date()));}}public void sessionDestroyed(HttpSessionEvent event) {ServletContext app = event.getSession().getServletContext();int count = Integer.parseInt(app.getAttribute("onLineCount").toString());count--;app.setAttribute("onLineCount", count);} }
    • 新建一個servlet處理
    @WebServlet(name = "SessionServlet",value = "/sessionCount") public class SessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html");//獲取上下文對象ServletContext servletContext = this.getServletContext();Integer onLineCount = (Integer) servletContext.getAttribute("onLineCount");System.out.println("invoke doGet");PrintWriter out = resp.getWriter();out.println("<html><body>");out.println("<h1>" + onLineCount + "</h1>");out.println("</body></html>");} }
  • springboot監(jiān)聽器的使用(以實現異步Event監(jiān)聽為例子)
    • 場景

    很多時候當我們完成某些業(yè)務后需要給用戶推送相關消息提醒。對于這種非核心業(yè)務功能我們可以拿出來,創(chuàng)建一個事件去異步執(zhí)行,從而實現核心業(yè)務和子業(yè)務的解耦。

    • 實現

    定義事件類 Event

    創(chuàng)建一個類,繼承ApplicationEvent,并重寫構造函數。ApplicationEvent是Spring提供的所有應用程序事件擴展類。

    public class Event extends ApplicationEvent {private static final long serialVersionUID = 1L;private String msg ;private static final Logger logger=LoggerFactory.getLogger(Event.class);public Event(String msg) {super(msg);this.msg = msg;logger.info("add event success! message: {}", msg);}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;} }

    創(chuàng)建一個用于監(jiān)聽指定事件的類,需要實現ApplicationListener接口,說明它是一個應用程序事件的監(jiān)聽類。注意這里需要加上@Component注解,將其注入Spring容器中。

    @Component public class MyListener implements ApplicationListener<Event>{private static final Logger logger= LoggerFactory.getLogger(MyListener.class);@Overridepublic void onApplicationEvent(Event event) {logger.info("listener get event,sleep 2 second...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("event msg is:{}",event.getMsg());} }
    • 事件發(fā)布
      事件發(fā)布很簡單,只需要使用Spring 提供的ApplicationEventPublisher來發(fā)布自定義事件
    @RequestMapping("/notice/{msg}")@ResponseBodypublic void notice(@PathVariable String msg){logger.info("begin>>>>>");applicationEventPublisher.publishEvent(new Event(msg));logger.info("end<<<<<<<");} }
    • 測試
    • 異步執(zhí)行
      默認是沒有開啟異步的,我們需要手動配置開啟異步功能,很簡單,只需要在配置類上加上@EnableAsync注解就行了,該注解用于聲明啟用Spring的異步方法執(zhí)行功能,需要和@Configuration注解一起使用,我們可以直接加在啟動類上。然后在監(jiān)聽方法上加上@Async注解,說明當前方法使用異步去執(zhí)行。
    @Component public class MyListener implements ApplicationListener<Event>{private static final Logger logger= LoggerFactory.getLogger(MyListener.class);@Override@Asyncpublic void onApplicationEvent(Event event) {logger.info("listener get event,sleep 2 second...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("event msg is:{}",event.getMsg());} }


    可以發(fā)現已經實現了異步功能,主線程為nio-8080-exec-1,監(jiān)聽線程為 task-1。從瀏覽器反應可以看出,接口直接返回了,并沒有等監(jiān)聽線程執(zhí)行完后才返回。

    • 自定義異步線程池

    用默認的Spring線程池會有啥問題呢?SimpleAsyncTaskExecutor不是真的線程池,這個類不重用線程,每次調用都會創(chuàng)建一個新的線程。很有可能導致OOM。創(chuàng)建配置類(加上@Configuration),實現AsyncConfigurer接口,重寫Executor方法。這里我們可以將原先配置在啟動類上的@EnableAsync注解放到這個類上而不再直接加在主啟動類上。

    @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer {private static final Logger logger = LoggerFactory.getLogger(AsyncConfig.class);/*** 自定義異步線程池*/@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();//核心線程數taskExecutor.setCorePoolSize(2);//最大線程數taskExecutor.setMaxPoolSize(10);//隊列大小taskExecutor.setQueueCapacity(15);//線程名的前綴taskExecutor.setThreadNamePrefix("async-thread-");taskExecutor.initialize();return taskExecutor;}/*** 捕捉IllegalArgumentException異常* @return*/public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new MyAsyncExceptionHandler();}static class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler{@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... objects) {logger.info("TASK Exception message - " + throwable.getMessage());logger.info("Method name - " + method.getName());for (Object param : objects) {logger.info("Parameter value - " + param);}}} }

    六、 過濾器、攔截器、監(jiān)聽器對比

    參考文章
    參考文章
    參考文章
    參考文章
    參考文章

    總結

    以上是生活随笔為你收集整理的过滤器、拦截器、监听器的区别与使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。