過濾器
過濾器的英文名稱為 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 應用程式中物件的創建、銷毀等動作的發送,同時對監聽的情況作出相應的處理,最常用于統計網站的在線人數、訪問量等,關注公眾號Java技術堆疊回復boot可以獲取更多系列 Spring Boot 教程,
監聽器大概分為以下幾種:
-
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 的業務層來驗證用戶是否已經登錄,
本人免費整理了Java高級資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并發分布式等教程,一共30G,需要自己領取,
傳送門:https://shimo.im/docs/rQRKDPx6dPXqvYPq
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/90807.html
標籤:Java
