SpringBoot實現攔截器、過濾器、監聽器過程解析
這篇文章主要介紹了SpringBoot實現攔截器、過濾器、監聽器過程解析,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
過濾器
過濾器簡介
過濾器的英文名稱為 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,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,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 的業務層來驗證使用者是否已經登入。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。