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





PS:原文鏈接 https://www.cnblogs.com/haixiang/p/12000685.html,轉載請注明出處

過濾器

過濾器簡介

過濾器的英文名稱為 Filter, 是 Servlet 技術中最實用的技術。如同它的名字一樣,過濾器是處於客戶端和服務器資源文件之間的一道過濾網,幫助我們過濾掉一些不符合要求的請求,通常用作 Session 校驗,判斷用戶權限,如果不符合設定條件,則會被攔截到特殊的地址或者基於特殊的響應。

過濾器的使用

首先需要實現 Filter接口然后重寫它的三個方法

  • init 方法:在容器中創建當前過濾器的時候自動調用
  • destory 方法:在容器中銷毀當前過濾器的時候自動調用
  • doFilter 方法:過濾的具體操作

我們先引入 Maven 依賴,其中 lombok 是用來避免每個文件創建 Logger 來打印日志

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

我們首先實現接口,重寫三個方法,對包含我們要求的四個請求予以放行,將其它請求攔截重定向至/online,只要在將MyFilter實例化后即可,我們在后面整合代碼中一起給出。

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;

@Log4j2
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化過濾器");
    }
  
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response);
        String requestUri = request.getRequestURI();
        log.info("請求地址是:"+requestUri);
        if (requestUri.contains("/addSession")
            || requestUri.contains("/removeSession")
            || requestUri.contains("/online")
            || requestUri.contains("/favicon.ico")) {
            filterChain.doFilter(servletRequest, response);
        } else {
            wrapper.sendRedirect("/online");
        }
    }
  
    @Override
    public void destroy() {
        //在服務關閉時銷毀
        log.info("銷毀過濾器");
    }
}

攔截器

攔截器介紹

Java中的攔截器是動態攔截 action 調用的對象,然后提供了可以在 action 執行前后增加一些操作,也可以在 action 執行前停止操作,功能與過濾器類似,但是標准和實現方式不同。

  • 登錄認證:在一些應用中,可能會通過攔截器來驗證用戶的登錄狀態,如果沒有登錄或者登錄失敗,就會給用戶一個友好的提示或者返回登錄頁面,當然大型項目中都不采用這種方式,都是調單點登錄系統接口來驗證用戶。
  • 記錄系統日志:我們在常見應用中,通常要記錄用戶的請求信息,比如請求 ip,方法執行時間等,通過這些記錄可以監控系統的狀況,以便於對系統進行信息監控、信息統計、計算 PV、性能調優等。
  • 通用處理:在應用程序中可能存在所有方法都要返回的信息,這是可以利用攔截器來實現,省去每個方法冗余重復的代碼實現。

使用攔截器

我們需要實現 HandlerInterceptor 類,並且重寫三個方法

  • preHandle:在 Controoler 處理請求之前被調用,返回值是 boolean類型,如果是true就進行下一步操作;若返回false,則證明不符合攔截條件,在失敗的時候不會包含任何響應,此時需要調用對應的response返回對應響應。
  • postHandler:在 Controoler 處理請求執行完成后、生成視圖前執行,可以通過ModelAndView對視圖進行處理,當然ModelAndView也可以設置為 null。
  • afterCompletion:在 DispatcherServlet 完全處理請求后被調用,通常用於記錄消耗時間,也可以對一些資源進行處理。
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Log4j2
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("【MyInterceptor】調用了:{}", request.getRequestURI());
        request.setAttribute("requestTime", System.currentTimeMillis());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        if (!request.getRequestURI().contains("/online")) {
            HttpSession session = request.getSession();
            String sessionName = (String) session.getAttribute("name");
            if ("haixiang".equals(sessionName)) {
                log.info("【MyInterceptor】當前瀏覽器存在 session:{}",sessionName);
            }
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        long duration = (System.currentTimeMillis() - (Long)request.getAttribute("requestTime"));
        log.info("【MyInterceptor】[{}]調用耗時:{}ms",request.getRequestURI(), duration);
    }
}

監聽器

監聽器簡介

監聽器通常用於監聽 Web 應用程序中對象的創建、銷毀等動作的發送,同時對監聽的情況作出相應的處理,最常用於統計網站的在線人數、訪問量等。

監聽器大概分為以下幾種:

  • ServletContextListener:用來監聽 ServletContext 屬性的操作,比如新增、修改、刪除。
  • HttpSessionListener:用來監聽 Web 應用種的 Session 對象,通常用於統計在線情況。
  • ServletRequestListener:用來監聽 Request 對象的屬性操作。

監聽器的使用

我們通過 HttpSessionListener來統計當前在線人數、ip等信息,為了避免並發問題我們使用原子int來計數。

ServletContext,是一個全局的儲存信息的空間,它的生命周期與Servlet容器也就是服務器保持一致,服務器關閉才銷毀。request,一個用戶可有多個;session,一個用戶一個;而servletContext,所有用戶共用一個。所以,為了節省空間,提高效率,ServletContext中,要放必須的、重要的、所有用戶需要共享的線程又是安全的一些信息。因此我們這里用ServletContext來存儲在線人數sessionCount最為合適。

我們下面來統計當前在線人數

import lombok.extern.log4j.Log4j2;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.concurrent.atomic.AtomicInteger;

@Log4j2
public class MyHttpSessionListener implements HttpSessionListener {

    public static AtomicInteger userCount = new AtomicInteger(0);

    @Override
    public synchronized void sessionCreated(HttpSessionEvent se) {
        userCount.getAndIncrement();
        se.getSession().getServletContext().setAttribute("sessionCount", userCount.get());
        log.info("【在線人數】人數增加為:{}",userCount.get());
      
        //此處可以在ServletContext域對象中為訪問量計數,然后傳入過濾器的銷毀方法
        //在銷毀方法中調用數據庫入庫,因為過濾器生命周期與容器一致
    }

    @Override
    public synchronized void sessionDestroyed(HttpSessionEvent se) {
        userCount.getAndDecrement();
        se.getSession().getServletContext().setAttribute("sessionCount", userCount.get());
        log.info("【在線人數】人數減少為:{}",userCount.get());
    }
}

過濾器、攔截器、監聽器注冊

實例化三器

import com.anqi.tool.sanqi.filter.MyFilter;
import com.anqi.tool.sanqi.interceptor.MyInterceptor;
import com.anqi.tool.sanqi.listener.MyHttpRequestListener;
import com.anqi.tool.sanqi.listener.MyHttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor);
    }

    /**
     * 注冊過濾器
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new MyFilter());
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    /**
     * 注冊監聽器
     * @return
     */
    @Bean
    public ServletListenerRegistrationBean registrationBean(){
        ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean();
        registrationBean.setListener(new MyHttpRequestListener());
        registrationBean.setListener(new MyHttpSessionListener());
        return registrationBean;
    }
}

測試

import com.anqi.tool.sanqi.listener.MyHttpSessionListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@RestController
public class TestController {

    @GetMapping("addSession")
    public String addSession(HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.setAttribute("name", "haixiang");
        return "當前在線人數" + session.getServletContext().getAttribute("sessionCount") + "人";
    }

    @GetMapping("removeSession")
    public String removeSession(HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.invalidate();
        return "當前在線人數" + session.getServletContext().getAttribute("sessionCount") + "人";
    }

    @GetMapping("online")
    public String online() {
        return "當前在線人數" + MyHttpSessionListener.userCount.get() + "人";
    }

}

以下是監聽請求的監聽器

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;

public class MyHttpRequestListener implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("request 監聽器被銷毀");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
        String requestURI = req.getRequestURI();
        System.out.println(requestURI+"--"+"被調用");
    }
}

攔截器與過濾器的區別

1.參考標准

  • 過濾器是 JavaEE 的標准,依賴於 Servlet 容器,生命周期也與容器一致,利用這一特性可以在銷毀時釋放資源或者數據入庫。
  • 攔截器是SpringMVC中的內容,依賴於web框架,通常用於驗證用戶權限或者記錄日志,但是這些功能也可以利用 AOP 來代替。

2.實現方式

  • 過濾器是基於回調函數實現,無法注入 ioc 容器中的 bean。
  • 攔截器是基於反射來實現,因此攔截器中可以注入 ioc 容器中的 bean,例如注入 Redis 的業務層來驗證用戶是否已經登錄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM