丨springweb過濾器使用常見錯誤上_第1頁
丨springweb過濾器使用常見錯誤上_第2頁
丨springweb過濾器使用常見錯誤上_第3頁
丨springweb過濾器使用常見錯誤上_第4頁
丨springweb過濾器使用常見錯誤上_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

案例1:@WebFilter代public代publicclassTimeCostFilterimplementsFilter4public56}78publicvoiddoFilter(ServletRequestrequest,ServletResponse9longstart=chain.doFilter(request,longend=longtime=end-System.out.println("執(zhí)行時間(ms):"+}16@WebFilter。所以在啟動程序中,我們需要加上掃描注解(即6SpringApplication.run(Application.class,789}然后,我們提供了一個StudentController接口來供學(xué)生代代33456789publicclassStudentControllerpublicStringsaveUser(Stringname)throws{return}}TimeCostFilter(ElasticeSearch/InfluxDB)里面去,我們可能會添加這樣一個Service類:代代12345678publicclassMetricsServicepublicTimeCostFilter}完成后你會發(fā)現(xiàn),SpringBootAPPLICATIONFAILEDTOSTARTFieldtimeCostFilterincom.spring.puzzle.web.filter.example1.MetricsServicerequiredabeanoftype'com.spring.puzzle.web.filter.example1.TimeCostFilter'thatcouldnotbefound.TimeCostFilterBean,本質(zhì)上,過濾器被@WebFilter修飾后,TimeCostFilter只會被包裝為FilterRegistrationBeanTimeCostFilterInnerBean這意味著TimeCostFilter實例并不會作為Bean到Spring容器。TimeCostFilterTimeCostFilter是怎么實例化,并和FilterRegistrationBean關(guān)聯(lián)起來的?我們先來看第一個問題:FilterRegistrationBean是什么?它是如何定義的?實際上,WebFilter的全名是javax.servlet.annotation.WebFilter,很明顯,它并不屬于Spring,而是Servlet的規(guī)范。當(dāng)SpringBoot項目中使用它時,SpringBoot使用了org.springframework.boot.web.servlet.FilterRegistrationBean@WebFilter記的實例。從實現(xiàn)上來說,即FilterRegistrationBean#Filter@WebFilterFilterFilter的名稱作為Bean的名字來指向它。但是調(diào)試下你會發(fā)現(xiàn),在SpringBoot中,Bean名字確實是對的,只是Bean實例其實是FilterRegistrationBean。那么這個FilterRegistrationBean最早是如何獲取的呢?這還得追溯到@WebFilter這個注解是如何被處理的。在具體解析之前,我們先看下@WebFilter是如何工作起來的。使用@WebFilter時,F(xiàn)ilter了在能 ponentScan掃到的路徑之下@WebFilterWebFilterHandler接在doHandle()中加入斷點,開始調(diào)試,執(zhí)行調(diào)用棧如下:從堆棧上,我們可以看出對@WebFilter的處理是在SpringBoot啟動時,而處理的觸發(fā) ponentRegisteringPostProcessor這個類。它繼承了BeanFactoryPostProcessor接口,實現(xiàn)對@WebFilter、@WebListener、@WebServlet的掃描和處理,其中對于@WebFilter的處理使用的就是上文中提到的1123456789代ponentRegisteringPostProcessorimplementsprivatestaticfinalstaticponentHandler>ponentHandlers=newArrayList<>=}//publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactif(isRunningInEmbeddedWebServer()){ponentProvidercomponentProvider= (BeanDefinitionRegistry) 最終,WebServletHandler通過父類 ponentHandler的模版方法模式,處理了所有被@WebFilter注解的類,關(guān)鍵代碼如下:代代publicvoiddoHandle(Map<String,Object>attributes,AnnotatedBeanDefinitionBeanDefinitionRegistryregistry)BeanDefinitionBuilderbuilder=builder.addPropertyValue("asyncSupported",builder.addPropertyValue("dispatcherTypes",builder.addPropertyValue("filter",builder.addPropertyValue("urlPatterns",registry.registerBeanDefinition(name,10從這里,我們第一次看到了FilterRegistrationBean。通過調(diào)試上述代碼的最后一行,可以看到,最終我們的FilterRegistrationBean,其名字就是我們定義的WebFilter的后續(xù)這個Bean的具體創(chuàng)建過程,這里不再贅述,感的話你可以繼續(xù)深入研究?,F(xiàn)在,我們接著看第二個問題:TimeCostFilter何時被實例化?BeanFilterRegistrationBean,但是TimeCostFilter是何時實例化的呢?為什么它沒有成為一個普通的Bean?TimeCostFilterTomcatFilterRegistrationBean(createBean)TimeCostFilter身,TimeCostFilter是通過ResolveInnerBean來創(chuàng)建的;TimeCostFilterInnerBean,我們可以通過下面的調(diào)試視圖看到它的一通過上述分析,你可以看出最終TimeCostFilter實例是一種InnerBean,所以自動注入從上述的解析中,我們可以了解到,當(dāng)使用@WebFilter修飾過濾器時,TimeCostFilter類型的Bean并沒有到Spring容器中,真正的是FilterRegistrationBean。這里考慮到可能存在多個Filter,所以我們可以這樣修改下案例代碼:代代12345678publicclass{@AutowiredFilterRegistrationBean}注入的類型是FilterRegistrationBean類型,而不是TimeCostFiltercom.spring.puzzle.filter.TimeCostFilter(不能用案例2:Filter中不多次執(zhí)行 ponentScan+@WebFilter構(gòu)建而在實際生產(chǎn)過程中,如果我們需要構(gòu)建的過濾器是針對全局路徑有效,且沒有任何特殊需求(主要是指對rvt0的一些異步特性支持),那么你完全可以直接使用r接口(或者繼承ng對r接口的包裝類OncePerRequestFtrponentpringBn,也是可以達(dá)到預(yù)期的需求。 ponentScan+@WebFilter,這里我們不妨再以@Component+Filter接口的實現(xiàn)方式來呈現(xiàn)下我們的案例,也好讓你對Filter的使用能SpringBootWeb4SpringApplication.run(LearningApplication.class,567}StudentController保持功能不變,所以你可以直接參考之前的代碼。另外我們定義一個DemoFilter用來模擬問題,這個Filter標(biāo)記了@Component且實現(xiàn)了Filter接口,已代代publicclassDemoFilterimplementsFilter4{56System.out.println("Filter處理中時發(fā)生異常7thrownew8}catch(Exceptione)9chain.doFilter(request,}chain.doFilter(request,}13代代1Filter..................用 ..................用 我們本來的設(shè)計目標(biāo)是希望r的業(yè)務(wù)行不會影響到業(yè)務(wù)的執(zhí)行,所以當(dāng)拋出異常時,我們還是會調(diào)用doFilterdoFilter,最終導(dǎo)致我們的r執(zhí)行多次。r在解析之前,我先給你講下Filter以Tomcat為例,我們先來看下它的Filter實現(xiàn)中最重要的類ApplicationFilterChain。但區(qū)別在于遞歸調(diào)用是同一個對象把子任務(wù)交給同一個方法本身去完成,而職責(zé)鏈則是一個對象把子任務(wù)交給其他對象的同名方法去完成。其在于上下文nFilter不同業(yè)務(wù)場景的處理,達(dá)到業(yè)務(wù)解耦。整個FilterChain的結(jié)構(gòu)就像這張圖一樣:這里我們不妨還是帶著兩個問題去理解FilterChainStandardWrapperValve#invoke(),快速解決第一8{9((servlet!=null)&&(filterChain!=null))//Swallowoutputifif(context.getSwallowOutput())////}18//1919通過代碼可以看出,SpringApplicationFilterFactory.createFilterChain(FilterChain,然后調(diào)用其doFilter()執(zhí)行責(zé)任鏈。而這些步驟的起始點正是FilterChain首先查看ApplicationFilterFactory.createFilterChain(),來看下FilterChain如何被創(chuàng)12123456789代publicstaticApplicationFilterChaincreateFilterChain(ServletRequestrequest,Wrapperwrapper,Servletservlet){//ApplicationFilterChainfilterChain=null;if(requestinstanceofRequest){////創(chuàng)建filterChain=new//}////Addtherelevantpath-mappedfilterstothisfilterchainfor(inti=0;i<filterMaps.length;i++){//ApplicationFilterConfigfilterConfig=//return}if(filterConfig=={}增加filterConfig到}類及其89(n==filters.length)ApplicationFilterConfig[]newFiltersnewApplicationFilterConfig[n+System.arraycopy(filters,0,newFilters,0,filters= filters[n++]=18在ApplicationFilterChain里,了3個變量,類型為ApplicationFilterConfig的數(shù)組Filters、過濾器總數(shù)計數(shù)器n,以及標(biāo)識運行過程中被執(zhí)行過的過濾器個數(shù)pos。每個被初始化的Filter都會通過filterChain.addFilter(),加入到類型為ApplicationFilterConfigFiltersFiltern,使其等于Filters數(shù)組的長度。到這,Spring就完成了FilterChain的創(chuàng)建準(zhǔn)備工作。接下來,我們繼續(xù)看FilterChain的執(zhí)行細(xì)節(jié),即ApplicationFilterChain的if(Globals.IS_SECURITY_ENABLED)}else 10這里邏輯被委派到了當(dāng)前類的私有方法internalDoFilter4//pos5ApplicationFilterConfigfilterConfig=6try7Filterfilter=8//9//執(zhí)行filter.doFilter(request,response,//}//}//servlet.service(request, 20ApplicationFilterChain的internalDoFilter()是過濾器邏輯的ApplicationFilterChain的成員變量Filters了所有用戶定義的過濾器ApplicationFilterChain的類成員變量n為過濾器總數(shù),變量pos是運行過程中已經(jīng)執(zhí)internalDoFilter()每被調(diào)用一次,pos變量值自增1,即從類成員變量Filters中取下一個Filter;filter.doFilter(request,response,this)會調(diào)用過濾器實現(xiàn)的doFilter(),注意第三個參數(shù)值為this,即為當(dāng)前ApplicationFilterChain實例,這意味著:用戶需要在過濾器中顯式調(diào)用一次javax.servlet.FilterChain#doFilter,才能完成整個鏈路;pos<n意味著執(zhí)行完所有的過濾器,才能通過servlet.service(request,response)去servlet.service(request,responseinternalDoFilter()執(zhí)行到了Controller層的saveUser(DemoFilterdoFilter()try一次,因而當(dāng)拋出異常的時候,doFilter()明顯會被執(zhí)行兩次,相對應(yīng)的servlet.service(request,response)方法以及對應(yīng)的Controller處理方法也被執(zhí)行了兩filterChain.doFilter(request,response)就可以了,于是代碼就變成了這樣:代代publicclassDemoFilterimplementsFilterpublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,try556789System.out.println("Filter處理中時發(fā)生異常thrownew}catch(Exceptione)//chain.doFilter(request,}chain.doFilter(request,}}@WebFilter這種方式構(gòu)建的Filter是無法直接根據(jù)過濾器定義類型來自動注入的,因為這種Filter本身是以內(nèi)部Bean來呈現(xiàn)的,它最終是通過FilterRegistrationBean來呈現(xiàn)給Spring的。所以我們可以通過自動注入FilterRegistrationBean類型來完成裝代代123FilterRegistrationBean我們在過濾器的執(zhí)行中,一定要注意避免不要多次調(diào)用doFilter(于“不”,但是要理解這個問題呈現(xiàn)的現(xiàn)象,就必須對過濾器的流程有所了解。可以看過濾器執(zhí)行的流程圖:當(dāng)一個請求來臨時,會執(zhí)行到StandardWrapperValve的invoke(),這個方創(chuàng)建ApplicationFilterChain,并通過ApplicationFilterChain#doFilter()觸發(fā)過濾器執(zhí)ApplicationFilterChain的doFilter()internalDoFilt

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論