SpringBoot實(shí)現(xiàn)攔截器、過(guò)濾器、監(jiān)聽器過(guò)程解析
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)攔截器、過(guò)濾器、監(jiān)聽器過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
過(guò)濾器
過(guò)濾器簡(jiǎn)介
過(guò)濾器的英文名稱為 Filter, 是 Servlet 技術(shù)中最實(shí)用的技術(shù)。如同它的名字一樣,過(guò)濾器是處于客戶端和服務(wù)器資源文件之間的一道過(guò)濾網(wǎng),幫助我們過(guò)濾掉一些不符合要求的請(qǐng)求,通常用作 Session 校驗(yàn),判斷用戶權(quán)限,如果不符合設(shè)定條件,則會(huì)被攔截到特殊的地址或者基于特殊的響應(yīng)。
過(guò)濾器的使用
首先需要實(shí)現(xiàn) Filter接口然后重寫它的三個(gè)方法
- init 方法:在容器中創(chuàng)建當(dāng)前過(guò)濾器的時(shí)候自動(dòng)調(diào)用
- destory 方法:在容器中銷毀當(dāng)前過(guò)濾器的時(shí)候自動(dòng)調(diào)用
- doFilter 方法:過(guò)濾的具體操作
我們先引入 Maven 依賴,其中 lombok 是用來(lái)避免每個(gè)文件創(chuàng)建 Logger 來(lái)打印日志
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
我們首先實(shí)現(xiàn)接口,重寫三個(gè)方法,對(duì)包含我們要求的四個(gè)請(qǐng)求予以放行,將其它請(qǐng)求攔截重定向至/online,只要在將MyFilter實(shí)例化后即可,我們?cè)诤竺嬲洗a中一起給出。
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("初始化過(guò)濾器"); } @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("請(qǐng)求地址是:"+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() { //在服務(wù)關(guān)閉時(shí)銷毀 log.info("銷毀過(guò)濾器"); } }
攔截器
攔截器介紹
Java中的攔截器是動(dòng)態(tài)攔截 action 調(diào)用的對(duì)象,然后提供了可以在 action 執(zhí)行前后增加一些操作,也可以在 action 執(zhí)行前停止操作,功能與過(guò)濾器類似,但是標(biāo)準(zhǔn)和實(shí)現(xiàn)方式不同。
- 登錄認(rèn)證:在一些應(yīng)用中,可能會(huì)通過(guò)攔截器來(lái)驗(yàn)證用戶的登錄狀態(tài),如果沒有登錄或者登錄失敗,就會(huì)給用戶一個(gè)友好的提示或者返回登錄頁(yè)面,當(dāng)然大型項(xiàng)目中都不采用這種方式,都是調(diào)單點(diǎn)登錄系統(tǒng)接口來(lái)驗(yàn)證用戶。
- 記錄系統(tǒng)日志:我們?cè)诔R姂?yīng)用中,通常要記錄用戶的請(qǐng)求信息,比如請(qǐng)求 ip,方法執(zhí)行時(shí)間等,通過(guò)這些記錄可以監(jiān)控系統(tǒng)的狀況,以便于對(duì)系統(tǒng)進(jìn)行信息監(jiān)控、信息統(tǒng)計(jì)、計(jì)算 PV、性能調(diào)優(yōu)等。
- 通用處理:在應(yīng)用程序中可能存在所有方法都要返回的信息,這是可以利用攔截器來(lái)實(shí)現(xiàn),省去每個(gè)方法冗余重復(fù)的代碼實(shí)現(xiàn)。
使用攔截器
我們需要實(shí)現(xiàn) HandlerInterceptor 類,并且重寫三個(gè)方法
- preHandle:在 Controoler 處理請(qǐng)求之前被調(diào)用,返回值是 boolean類型,如果是true就進(jìn)行下一步操作;若返回false,則證明不符合攔截條件,在失敗的時(shí)候不會(huì)包含任何響應(yīng),此時(shí)需要調(diào)用對(duì)應(yīng)的response返回對(duì)應(yīng)響應(yīng)。
- postHandler:在 Controoler 處理請(qǐng)求執(zhí)行完成后、生成視圖前執(zhí)行,可以通過(guò)ModelAndView對(duì)視圖進(jìn)行處理,當(dāng)然ModelAndView也可以設(shè)置為 null。
- afterCompletion:在 DispatcherServlet 完全處理請(qǐng)求后被調(diào)用,通常用于記錄消耗時(shí)間,也可以對(duì)一些資源進(jìn)行處理。
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】調(diào)用了:{}", 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】當(dāng)前瀏覽器存在 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】[{}]調(diào)用耗時(shí):{}ms",request.getRequestURI(), duration); } }
監(jiān)聽器
監(jiān)聽器簡(jiǎn)介
監(jiān)聽器通常用于監(jiān)聽 Web 應(yīng)用程序中對(duì)象的創(chuàng)建、銷毀等動(dòng)作的發(fā)送,同時(shí)對(duì)監(jiān)聽的情況作出相應(yīng)的處理,最常用于統(tǒng)計(jì)網(wǎng)站的在線人數(shù)、訪問(wèn)量等。
監(jiān)聽器大概分為以下幾種:
- ServletContextListener:用來(lái)監(jiān)聽 ServletContext 屬性的操作,比如新增、修改、刪除。
- HttpSessionListener:用來(lái)監(jiān)聽 Web 應(yīng)用種的 Session 對(duì)象,通常用于統(tǒng)計(jì)在線情況。
- ServletRequestListener:用來(lái)監(jiān)聽 Request 對(duì)象的屬性操作。
監(jiān)聽器的使用
我們通過(guò) HttpSessionListener來(lái)統(tǒng)計(jì)當(dāng)前在線人數(shù)、ip等信息,為了避免并發(fā)問(wèn)題我們使用原子int來(lái)計(jì)數(shù)。
ServletContext,是一個(gè)全局的儲(chǔ)存信息的空間,它的生命周期與Servlet容器也就是服務(wù)器保持一致,服務(wù)器關(guān)閉才銷毀。request,一個(gè)用戶可有多個(gè);session,一個(gè)用戶一個(gè);而servletContext,所有用戶共用一個(gè)。所以,為了節(jié)省空間,提高效率,ServletContext中,要放必須的、重要的、所有用戶需要共享的線程又是安全的一些信息。因此我們這里用ServletContext來(lái)存儲(chǔ)在線人數(shù)sessionCount最為合適。
我們下面來(lái)統(tǒng)計(jì)當(dāng)前在線人數(shù)
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("【在線人數(shù)】人數(shù)增加為:{}",userCount.get()); //此處可以在ServletContext域?qū)ο笾袨樵L問(wèn)量計(jì)數(shù),然后傳入過(guò)濾器的銷毀方法 //在銷毀方法中調(diào)用數(shù)據(jù)庫(kù)入庫(kù),因?yàn)檫^(guò)濾器生命周期與容器一致 } @Override public synchronized void sessionDestroyed(HttpSessionEvent se) { userCount.getAndDecrement(); se.getSession().getServletContext().setAttribute("sessionCount", userCount.get()); log.info("【在線人數(shù)】人數(shù)減少為:{}",userCount.get()); } }
過(guò)濾器、攔截器、監(jiān)聽器注冊(cè)
實(shí)例化三器
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); } /** * 注冊(cè)過(guò)濾器 * @return */ @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); filterRegistration.setFilter(new MyFilter()); filterRegistration.addUrlPatterns("/*"); return filterRegistration; } /** * 注冊(cè)監(jiān)聽器 * @return */ @Bean public ServletListenerRegistrationBean registrationBean(){ ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean(); registrationBean.setListener(new MyHttpRequestListener()); registrationBean.setListener(new MyHttpSessionListener()); return registrationBean; } }
測(cè)試
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 "當(dāng)前在線人數(shù)" + session.getServletContext().getAttribute("sessionCount") + "人"; } @GetMapping("removeSession") public String removeSession(HttpServletRequest request) { HttpSession session = request.getSession(); session.invalidate(); return "當(dāng)前在線人數(shù)" + session.getServletContext().getAttribute("sessionCount") + "人"; } @GetMapping("online") public String online() { return "當(dāng)前在線人數(shù)" + MyHttpSessionListener.userCount.get() + "人"; } }
以下是監(jiān)聽請(qǐng)求的監(jiān)聽器
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 監(jiān)聽器被銷毀"); } @Override public void requestInitialized(ServletRequestEvent sre) { HttpServletRequest req = (HttpServletRequest) sre.getServletRequest(); String requestURI = req.getRequestURI(); System.out.println(requestURI+"--"+"被調(diào)用"); } }
攔截器與過(guò)濾器的區(qū)別
1.參考標(biāo)準(zhǔn)
- 過(guò)濾器是 JavaEE 的標(biāo)準(zhǔn),依賴于 Servlet 容器,生命周期也與容器一致,利用這一特性可以在銷毀時(shí)釋放資源或者數(shù)據(jù)入庫(kù)。
- 攔截器是SpringMVC中的內(nèi)容,依賴于web框架,通常用于驗(yàn)證用戶權(quán)限或者記錄日志,但是這些功能也可以利用 AOP 來(lái)代替。
2.實(shí)現(xiàn)方式
- 過(guò)濾器是基于回調(diào)函數(shù)實(shí)現(xiàn),無(wú)法注入 ioc 容器中的 bean。
- 攔截器是基于反射來(lái)實(shí)現(xiàn),因此攔截器中可以注入 ioc 容器中的 bean,例如注入 Redis 的業(yè)務(wù)層來(lái)驗(yàn)證用戶是否已經(jīng)登錄。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA中常用的設(shè)計(jì)模式:?jiǎn)卫J?,工廠模式,觀察者模式
設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用。設(shè)計(jì)模式是軟件開發(fā)人員在軟件開發(fā)過(guò)程中面臨的一般問(wèn)題的解決方案。這些解決方案是眾多軟件開發(fā)人員經(jīng)過(guò)相當(dāng)長(zhǎng)的一段時(shí)間的試驗(yàn)和錯(cuò)誤總結(jié)出來(lái)的。2020-04-04實(shí)例解析使用Java實(shí)現(xiàn)基本的音頻播放器的編寫要點(diǎn)
這篇文章主要介紹了使用Java實(shí)現(xiàn)基本的音頻播放器的代碼要點(diǎn)實(shí)例分享,包括音頻文件的循環(huán)播放等功能實(shí)現(xiàn)的關(guān)鍵點(diǎn),需要的朋友可以參考下2016-01-01使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題
這篇文章主要介紹了使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java RMI引起的log4j漏洞問(wèn)題重現(xiàn)
java的log4j框架出現(xiàn)了一個(gè)大漏洞對(duì)服務(wù)器案例引起了不小的影響,如果你使用的是spring框架,用的是logback,不受這個(gè)問(wèn)題的影響,下面跟著小編看下Java RMI引起的log4j漏洞問(wèn)題重現(xiàn),感興趣的朋友一起看看吧2021-12-12Java二維碼登錄流程實(shí)現(xiàn)代碼(包含短地址生成,含部分代碼)
近年來(lái),二維碼的使用越來(lái)越風(fēng)生水起,本篇文章主要介紹了Java二維碼登錄流程實(shí)現(xiàn)代碼,其中包含短地址生成,有興趣的可以了解一下。2016-12-12SpringBoot使用Filter實(shí)現(xiàn)簽名認(rèn)證鑒權(quán)的示例代碼
這篇文章主要介紹了SpringBoot使用Filter實(shí)現(xiàn)簽名認(rèn)證鑒權(quán)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04淺談mybatisPlus的Ipage分頁(yè)和map參數(shù)的問(wèn)題
這篇文章主要介紹了mybatisPlus的Ipage分頁(yè)和map參數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12