日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot实现过滤器、拦截器与切片源码分析

發(fā)布時間:2025/3/21 javascript 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot实现过滤器、拦截器与切片源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

過濾器Filter


過濾器概念

Filter是J2E中來的,可以看做是Servlet的一種“加強(qiáng)版”,它主要用于對用戶請求進(jìn)行預(yù)處理和后處理,擁有一個典型的處理鏈。Filter也可以對用戶請求生成響應(yīng),這一點與Servlet相同,但實際上很少會使用Filter向用戶請求生成響應(yīng)。使用Filter完整的流程是:Filter對用戶請求進(jìn)行預(yù)處理,接著將請求交給Servlet進(jìn)行預(yù)處理并生成響應(yīng),最后Filter再對服務(wù)器響應(yīng)進(jìn)行后處理。

過濾器作用

在JavaDoc中給出了幾種過濾器的作用

* Examples that have been identified for this design are* 1) Authentication Filters, 即用戶訪問權(quán)限過濾* 2) Logging and Auditing Filters, 日志過濾,可以記錄特殊用戶的特殊請求的記錄等* 3) Image conversion Filters* 4) Data compression Filters <br>* 5) Encryption Filters <br>* 6) Tokenizing Filters <br>* 7) Filters that trigger resource access events <br>* 8) XSL/T filters <br>* 9) Mime-type chain Filter <br>

對于第一條,即使用Filter作權(quán)限過濾,其可以這么實現(xiàn):定義一個Filter,獲取每個客戶端發(fā)起的請求URL,與當(dāng)前用戶無權(quán)限訪問的URL列表(可以是從DB中取出)作對比,起到權(quán)限過濾的作用。

過濾器實現(xiàn)方式

自定義的過濾器都必須實現(xiàn)javax.Servlet.Filter接口,并重寫接口中定義的三個方法:

  • void init(FilterConfig config):用于完成Filter的初始化。

  • void destory():用于Filter銷毀前,完成某些資源的回收。

  • void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):實現(xiàn)過濾功能,即對每個請求及響應(yīng)增加的額外的預(yù)處理和后處理。執(zhí)行該方法之前,即對用戶請求進(jìn)行預(yù)處理;執(zhí)行該方法之后,即對服務(wù)器響應(yīng)進(jìn)行后處理。值得注意的是,chain.doFilter()方法執(zhí)行之前為預(yù)處理階段,該方法執(zhí)行結(jié)束即代表用戶的請求已經(jīng)得到控制器處理。因此,如果在doFilter中忘記調(diào)用chain.doFilter()方法,則用戶的請求將得不到處理。

  • import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException;// 必須添加注解,springmvc通過web.xml配置 @Component public class TimeFilter implements Filter {private static final Logger LOG = LoggerFactory.getLogger(TimeFilter.class);@Overridepublic void init(FilterConfig filterConfig) throws ServletException {LOG.info("初始化過濾器:{}", filterConfig.getFilterName());}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {LOG.info("start to doFilter");long startTime = System.currentTimeMillis();chain.doFilter(request, response);long endTime = System.currentTimeMillis();LOG.info("the request of {} consumes {}ms.", getUrlFrom(request), (endTime - startTime));LOG.info("end to doFilter");}@Overridepublic void destroy() {LOG.info("銷毀過濾器");}private String getUrlFrom(ServletRequest servletRequest){if (servletRequest instanceof HttpServletRequest){return ((HttpServletRequest) servletRequest).getRequestURL().toString();}return "";} }

    從代碼中可看出,類Filter是在javax.servlet.*中,因此可以看出,過濾器的一個很大的局限性在于,其不能夠知道當(dāng)前用戶的請求是被哪個控制器(Controller)處理的,因為后者是spring框架中定義的。

    在SpringBoot中注冊第三方過濾器

    對于SpringMVC,可以通過在web.xml中注冊過濾器。但在SpringBoot中不存在web.xml,此時如果引用的某個jar包中的過濾器,且這個過濾器在實現(xiàn)時沒有使用@Component標(biāo)識為Spring Bean,則這個過濾器將不會生效。此時需要通過java代碼去注冊這個過濾器。以上面定義的TimeFilter為例,當(dāng)去掉類注解@Component時,注冊方式為:

    @Configuration public class WebConfig {/*** 注冊第三方過濾器* 功能與spring mvc中通過配置web.xml相同* @return*/@Beanpublic FilterRegistrationBean thirdFilter(){ThirdPartFilter thirdPartFilter = new ThirdPartFilter();FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;filterRegistrationBean.setFilter(thirdPartFilter);List<String > urls = new ArrayList<>();// 匹配所有請求路徑urls.add("/*");filterRegistrationBean.setUrlPatterns(urls);return filterRegistrationBean;} }

    相比使用@Component注解,這種配置方式有個優(yōu)點,即可以自由配置攔截的URL。

    ?

    攔截器Interceptor


    攔截器概念

    攔截器,在AOP(Aspect-Oriented Programming)中用于在某個方法或字段被訪問之前,進(jìn)行攔截,然后在之前或之后加入某些操作。攔截是AOP的一種實現(xiàn)策略。

    攔截器作用

  • 日志記錄:記錄請求信息的日志,以便進(jìn)行信息監(jiān)控、信息統(tǒng)計、計算PV(Page View)等

  • 權(quán)限檢查:如登錄檢測,進(jìn)入處理器檢測檢測是否登錄

  • 性能監(jiān)控:通過攔截器在進(jìn)入處理器之前記錄開始時間,在處理完后記錄結(jié)束時間,從而得到該請求的處理時間。(反向代理,如apache也可以自動記錄);

  • 通用行為:讀取cookie得到用戶信息并將用戶對象放入請求,從而方便后續(xù)流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現(xiàn)。

  • 攔截器實現(xiàn)

    通過實現(xiàn)HandlerInterceptor接口,并重寫該接口的三個方法來實現(xiàn)攔截器的自定義:

  • preHandler(HttpServletRequest request, HttpServletResponse response, Object handler):方法將在請求處理之前進(jìn)行調(diào)用。SpringMVC中的Interceptor同F(xiàn)ilter一樣都是鏈?zhǔn)秸{(diào)用。每個Interceptor的調(diào)用會依據(jù)它的聲明順序依次執(zhí)行,而且最先執(zhí)行的都是Interceptor中的preHandle方法,所以可以在這個方法中進(jìn)行一些前置初始化操作或者是對當(dāng)前請求的一個預(yù)處理,也可以在這個方法中進(jìn)行一些判斷來決定請求是否要繼續(xù)進(jìn)行下去。該方法的返回值是布爾值Boolean 類型的,當(dāng)它返回為false時,表示請求結(jié)束,后續(xù)的Interceptor和Controller都不會再執(zhí)行;當(dāng)返回值為true時就會繼續(xù)調(diào)用下一個Interceptor 的preHandle 方法,如果已經(jīng)是最后一個Interceptor 的時候就會是調(diào)用當(dāng)前請求的Controller 方法。

  • postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在當(dāng)前請求進(jìn)行處理之后,也就是Controller 方法調(diào)用之后執(zhí)行,但是它會在DispatcherServlet 進(jìn)行視圖返回渲染之前被調(diào)用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進(jìn)行操作。

  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):該方法也是需要當(dāng)前對應(yīng)的Interceptor的preHandle方法的返回值為true時才會執(zhí)行。顧名思義,該方法將在整個請求結(jié)束之后,也就是在DispatcherServlet?渲染了對應(yīng)的視圖之后執(zhí)行。這個方法的主要作用是用于進(jìn)行資源清理工作的。

  • @Component public class TimeInterceptor implements HandlerInterceptor {private static final Logger LOG = LoggerFactory.getLogger(TimeInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {LOG.info("在請求處理之前進(jìn)行調(diào)用(Controller方法調(diào)用之前)");request.setAttribute("startTime", System.currentTimeMillis());HandlerMethod handlerMethod = (HandlerMethod) handler;LOG.info("controller object is {}", handlerMethod.getBean().getClass().getName());LOG.info("controller method is {}", handlerMethod.getMethod());// 需要返回true,否則請求不會被控制器處理return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {LOG.info("請求處理之后進(jìn)行調(diào)用,但是在視圖被渲染之前(Controller方法調(diào)用之后),如果異常發(fā)生,則該方法不會被調(diào)用");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LOG.info("在整個請求結(jié)束之后被調(diào)用,也就是在DispatcherServlet 渲染了對應(yīng)的視圖之后執(zhí)行(主要是用于進(jìn)行資源清理工作)");long startTime = (long) request.getAttribute("startTime");LOG.info("time consume is {}", System.currentTimeMillis() - startTime);}

    與過濾器不同的是,攔截器使用@Component修飾后,還需要通過實現(xiàn)WebMvcConfigurer手動注冊:

    // java配置類 @Configuration public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TimeInterceptor timeInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(timeInterceptor);} }

    切片Aspect


    切片概述

    相比過濾器,攔截器能夠知道用戶發(fā)出的請求最終被哪個控制器處理,但是攔截器還有一個明顯的不足,即不能夠獲取request的參數(shù)以及控制器處理之后的response。所以就有了切片的用武之地了。

    切片實現(xiàn)

    切片的實現(xiàn)需要注意@Aspect,@Component以及@Around這三個注解的使用,詳細(xì)查看官方文檔:傳送門

    @Aspect @Component public class TimeAspect {private static final Logger LOG = LoggerFactory.getLogger(TimeAspect.class);@Around("execution(* me.ifight.controller.*.*(..))")public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{LOG.info("切片開始。。。");long startTime = System.currentTimeMillis();// 獲取請求入?yún)bject[] args = proceedingJoinPoint.getArgs();Arrays.stream(args).forEach(arg -> LOG.info("arg is {}", arg));// 獲取響應(yīng)Object response = proceedingJoinPoint.proceed();long endTime = System.currentTimeMillis();LOG.info("請求:{}, 耗時{}ms", proceedingJoinPoint.getSignature(), (endTime - startTime));LOG.info("切片結(jié)束。。。");return null;} }

    過濾器、攔截器以及切片的調(diào)用順序

    如下圖,展示了三者的調(diào)用順序Filter->Interceptor->Aspect->Controller。相反的是,當(dāng)Controller拋出的異常的處理順序則是從內(nèi)到外的。因此我們總是定義一個注解@ControllerAdvice去統(tǒng)一處理控制器拋出的異常。如果一旦異常被@ControllerAdvice處理了,則調(diào)用攔截器的afterCompletion方法的參數(shù)Exception ex就為空了。

    實際執(zhí)行的調(diào)用棧也說明了這一點:

    而對于過濾器和攔截器詳細(xì)的調(diào)用順序如下圖:

    過濾器和攔截器的區(qū)別

    最后有必要再說說過濾器和攔截器二者之間的區(qū)別:

    ?FilterInterceptor
    實現(xiàn)方式基于函數(shù)回調(diào)基于Java的反射機(jī)制的
    規(guī)范Servlet規(guī)范Spring規(guī)范
    作用范圍對幾乎所有的請求起作用只對action請求起作用

    除此之外,相比過濾器,攔截器能夠“看到”用戶的請求具體是被Spring框架的哪個控制器所處理。

    總結(jié)

    以上是生活随笔為你收集整理的SpringBoot实现过滤器、拦截器与切片源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。