Spring Boot 如何使用攔截器、過(guò)濾器、監(jiān)聽(tīng)器?
閱讀本文大概需要 7 分鐘。
作者:海向
出處:www.cnblogs.com/haixiang/p/12000685.html
過(guò)濾器
過(guò)濾器的使用
Filter接口然后重寫它的三個(gè)方法init 方法:在容器中創(chuàng)建當(dāng)前過(guò)濾器的時(shí)候自動(dòng)調(diào)用
destory 方法:在容器中銷毀當(dāng)前過(guò)濾器的時(shí)候自動(dòng)調(diào)用
doFilter 方法:過(guò)濾的具體操作
????org.projectlombok
????lombok
????org.springframework.boot
????spring-boot-starter-web
/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ò)濾器");
????}
}
攔截器
登錄認(rèn)證:在一些應(yīng)用中,可能會(huì)通過(guò)攔截器來(lái)驗(yàn)證用戶的登錄狀態(tài),如果沒(méi)有登錄或者登錄失敗,就會(huì)給用戶一個(gè)友好的提示或者返回登錄頁(yè)面,當(dāng)然大型項(xiàng)目中都不采用這種方式,都是調(diào)單點(diǎn)登錄系統(tǒng)接口來(lái)驗(yàn)證用戶。
記錄系統(tǒng)日志:我們?cè)诔R?jiàn)應(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)。
使用攔截器
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)聽(tīng)器
ServletContextListener:用來(lái)監(jiān)聽(tīng) ServletContext 屬性的操作,比如新增、修改、刪除。
HttpSessionListener:用來(lái)監(jiān)聽(tīng) Web 應(yīng)用種的 Session 對(duì)象,通常用于統(tǒng)計(jì)在線情況。
ServletRequestListener:用來(lái)監(jiān)聽(tīng) Request 對(duì)象的屬性操作。
監(jiān)聽(tīng)器的使用
HttpSessionListener來(lái)統(tǒng)計(jì)當(dāng)前在線人數(shù)、ip等信息,為了避免并發(fā)問(wèn)題我們使用原子int來(lái)計(jì)數(shù)。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("【在線人數(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)聽(tīng)器注冊(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)聽(tīng)器
?????*?@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()?+?"人";
????}
}
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)聽(tīng)器被銷毀");
????}
????@Override
????public?void?requestInitialized(ServletRequestEvent?sre)?{
????????HttpServletRequest?req?=?(HttpServletRequest)?sre.getServletRequest();
????????String?requestURI?=?req.getRequestURI();
????????System.out.println(requestURI+"--"+"被調(diào)用");
????}
}
攔截器與過(guò)濾器的區(qū)別
過(guò)濾器是 JavaEE 的標(biāo)準(zhǔn),依賴于?Servlet?容器,生命周期也與容器一致,利用這一特性可以在銷毀時(shí)釋放資源或者數(shù)據(jù)入庫(kù)。
攔截器是SpringMVC中的內(nèi)容,依賴于web框架,通常用于驗(yàn)證用戶權(quán)限或者記錄日志,但是這些功能也可以利用?AOP?來(lái)代替。
過(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)登錄。
MySQL中,當(dāng)update修改數(shù)據(jù)與原數(shù)據(jù)相同時(shí)會(huì)再次執(zhí)行嗎
內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬(wàn)并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper、數(shù)據(jù)結(jié)構(gòu)、限流熔斷降級(jí)......等技術(shù)棧!
?戳閱讀原文領(lǐng)取!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??朕已閱?

