中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站永久鏡像怎么做站長之家seo查找

網(wǎng)站永久鏡像怎么做,站長之家seo查找,簡(jiǎn)單的設(shè)計(jì)軟件,WordPress 先登錄我們都知道,過濾器是 Servlet 的重要標(biāo)準(zhǔn)之一,其在請(qǐng)求和響應(yīng)的統(tǒng)一處理、訪問日志記錄、請(qǐng)求權(quán)限審核等方面都有著不可替代的作用。在 Spring 編程中,我們主要就是配合使用ServletComponentScan 和 WebFilter 這兩個(gè)注解來構(gòu)建過濾器。 說起…

我們都知道,過濾器是 Servlet 的重要標(biāo)準(zhǔn)之一,其在請(qǐng)求和響應(yīng)的統(tǒng)一處理、訪問日志記錄、請(qǐng)求權(quán)限審核等方面都有著不可替代的作用。在 Spring 編程中,我們主要就是配合使用@ServletComponentScan 和 @WebFilter 這兩個(gè)注解來構(gòu)建過濾器。

說起來比較簡(jiǎn)單,好像只是標(biāo)記下這兩個(gè)注解就一勞永逸了。但是我們還是會(huì)遇到各式各樣的問題,例如工作不起來、順序不對(duì)、執(zhí)行多次等等都是常見的問題。這些問題的出現(xiàn)大多都是使用簡(jiǎn)單致使我們掉以輕心,只要你加強(qiáng)意識(shí),大概率就可以規(guī)避了。

那么接下來我們就來學(xué)習(xí)兩個(gè)典型的案例,并通過分析,帶你進(jìn)一步理解過濾器執(zhí)行的流程和原理

案例 1:@WebFilter 過濾器無法被自動(dòng)注入

假設(shè)我們要基于 Spring Boot 去開發(fā)一個(gè)學(xué)籍管理系統(tǒng)。為了統(tǒng)計(jì)接口耗時(shí),可以實(shí)現(xiàn)一個(gè)過濾器如下:

@WebFilter
@Slf4j
public class TimeCostFilter implements Filter {public TimeCostFilter(){System.out.println("construct");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("開始計(jì)算接口耗時(shí)");long start = System.currentTimeMillis();chain.doFilter(request, response);long end = System.currentTimeMillis();long time = end - start;System.out.println("執(zhí)行時(shí)間(ms):" + time);}
}

這個(gè)過濾器標(biāo)記了 @WebFilter。所以在啟動(dòng)程序中,我們需要加上掃描注解即 @ServletComponentScan)讓其生效,啟動(dòng)程序如下:

@SpringBootApplication
@ServletComponentScan
@Slf4j
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);log.info("啟動(dòng)成功");}
}

然后,我們提供了一個(gè) StudentController 接口來供學(xué)生注冊(cè):

@Controller
@Slf4j
public class StudentController {@PostMapping("/regStudent/{name}")@ResponseBodypublic String saveUser(String name) throws Exception {System.out.println("用戶注冊(cè)成功");return "success";}
}

上述程序完成后,你會(huì)發(fā)現(xiàn)一切按預(yù)期執(zhí)行。但是假設(shè)有一天,我們可能需要把 TimeCostFilter 記錄的統(tǒng)計(jì)數(shù)據(jù)輸出到專業(yè)的度量系統(tǒng)(ElasticeSearch/InfluxDB 等)里面去,我們可能會(huì)添加這樣一個(gè) Service 類:

@Service
public class MetricsService {@Autowiredpublic TimeCostFilter timeCostFilter;//省略其他非關(guān)鍵代碼}

完成后你會(huì)發(fā)現(xiàn),Spring Boot 都無法啟動(dòng)了:

***************************

APPLICATION FAILED TO START

***************************

?Description:?Field timeCostFilter in com.spring.puzzle.web.filter.example1.MetricsService required a bean of type 'com.spring.puzzle.web.filter.example1.TimeCostFilter' that could not be found.

?為什么會(huì)出現(xiàn)這樣的問題?既然 TimeCostFilter 生效了,看起來也像一個(gè)普通的 Bean,為什么不能被自動(dòng)注入?

案例解析

這次我們換個(gè)方式,我先告訴你結(jié)論,你可以暫停幾分鐘想想關(guān)鍵點(diǎn)。

本質(zhì)上,過濾器被 @WebFilter 修飾后,TimeCostFilter 只會(huì)被包裝為 FilterRegistrationBean,而 TimeCostFilter 自身,只會(huì)作為一個(gè) InnerBean 被實(shí)例化,這意味著 TimeCostFilter 實(shí)例并不會(huì)作為 Bean 注冊(cè)到 Spring 容器。

所以當(dāng)我們想自動(dòng)注入 TimeCostFilter 時(shí),就會(huì)失敗了。知道這個(gè)結(jié)論后,我們可以帶著兩個(gè)問題去理清一些關(guān)鍵的邏輯:

1.FilterRegistrationBean 是什么?它是如何被定義的?

2.TimeCostFilter 是怎么實(shí)例化,并和 FilterRegistrationBean 關(guān)聯(lián)起來的?

我們先來看第一個(gè)問題:FilterRegistrationBean 是什么?它是如何定義的?

實(shí)際上,WebFilter 的全名是 javax.servlet.annotation.WebFilter,很明顯,它并不屬于 Spring,而是 Servlet 的規(guī)范。當(dāng) Spring Boot 項(xiàng)目中使用它時(shí),Spring Boot 使用了 org.springframework.boot.web.servlet.FilterRegistrationBean 來包裝 @WebFilter 標(biāo)記的實(shí)例。從實(shí)現(xiàn)上來說,即 FilterRegistrationBean#Filter 屬性就是 @WebFilter 標(biāo)記的實(shí)例。這點(diǎn)我們可以從之前給出的截圖中看出端倪。

另外,當(dāng)我們定義一個(gè) Filter 類時(shí),我們可能想的是,我們會(huì)自動(dòng)生成它的實(shí)例,然后以 Filter 的名稱作為 Bean 的名字來指向它。但是調(diào)試下你會(huì)發(fā)現(xiàn),在 Spring Boot 中,Bean 名字確實(shí)是對(duì)的,只是 Bean 實(shí)例其實(shí)是 FilterRegistrationBean。

那么這個(gè) FilterRegistrationBean 最早是如何獲取的呢?這還得追溯到 @WebFilter 這個(gè)注解是如何被處理的。在具體解析之前,我們先看下 @WebFilter 是如何工作起來的。使用 @WebFilter 時(shí),Filter 被加載有兩個(gè)條件:

  • 聲明了 @WebFilter;
  • 在能被 @ServletComponentScan 掃到的路徑之下。

這里我們直接檢索對(duì) @WebFilter 的使用,可以發(fā)現(xiàn) WebFilterHandler 類使用了它,直接在 doHandle() 中加入斷點(diǎn),開始調(diào)試,執(zhí)行調(diào)用棧如下:

從堆棧上,我們可以看出對(duì) @WebFilter 的處理是在 Spring Boot 啟動(dòng)時(shí),而處理的觸發(fā)點(diǎn)是 ServletComponentRegisteringPostProcessor 這個(gè)類。它繼承了 BeanFactoryPostProcessor 接口,實(shí)現(xiàn)對(duì) @WebFilter、@WebListener、@WebServlet 的掃描和處理,其中對(duì)于 @WebFilter 的處理使用的就是上文中提到的 WebFilterHandler。這個(gè)邏輯可以參考下面的關(guān)鍵代碼:

class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {private static final List<ServletComponentHandler> HANDLERS;static {List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>();servletComponentHandlers.add(new WebServletHandler());servletComponentHandlers.add(new WebFilterHandler());servletComponentHandlers.add(new WebListenerHandler());HANDLERS = Collections.unmodifiableList(servletComponentHandlers);}// 省略非關(guān)鍵代碼@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if (isRunningInEmbeddedWebServer()) {ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();for (String packageToScan : this.packagesToScan) {scanPackage(componentProvider, packageToScan);}}}private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {// 掃描注解for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {if (candidate instanceof AnnotatedBeanDefinition) {// 使用 WebFilterHandler 等進(jìn)行處理for (ServletComponentHandler handler : HANDLERS) {handler.handle(((AnnotatedBeanDefinition) candidate),(BeanDefinitionRegistry) this.applicationContext);}}}}

最終,WebServletHandler 通過父類 ServletComponentHandler 的模版方法模式,處理了所有被 @WebFilter 注解的類,關(guān)鍵代碼如下:

public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterRegistrationBean.class);builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));builder.addPropertyValue("filter", beanDefinition);//省略其他非關(guān)鍵代碼builder.addPropertyValue("urlPatterns", extractUrlPatterns(attributes));registry.registerBeanDefinition(name, builder.getBeanDefinition());
}

從這里,我們第一次看到了 FilterRegistrationBean。通過調(diào)試上述代碼的最后一行,可以看到,最終我們注冊(cè)的 FilterRegistrationBean,其名字就是我們定義的 WebFilter 的名字:

后續(xù)這個(gè) Bean 的具體創(chuàng)建過程,這里不再贅述

現(xiàn)在,我們接著看第二個(gè)問題:TimeCostFilter 何時(shí)被實(shí)例化?

此時(shí),我們想要的 Bean 被“張冠李戴”成 FilterRegistrationBean,但是 TimeCostFilter 是何時(shí)實(shí)例化的呢?為什么它沒有成為一個(gè)普通的 Bean?

關(guān)于這點(diǎn),我們可以在 TimeCostFilter 的構(gòu)造器中加個(gè)斷點(diǎn),然后使用調(diào)試的方式快速定位到它的初始化時(shí)機(jī),這里我直接給出了調(diào)試截圖:

在上述的關(guān)鍵調(diào)用棧中,結(jié)合源碼,你可以找出一些關(guān)鍵信息:

1.?Tomcat 等容器啟動(dòng)時(shí),才會(huì)創(chuàng)建 FilterRegistrationBean;

2.?FilterRegistrationBean 在被創(chuàng)建時(shí)(createBean)會(huì)創(chuàng)建 TimeCostFilter 來裝配自身,TimeCostFilter 是通過 ResolveInnerBean 來創(chuàng)建的;

3.?TimeCostFilter 實(shí)例最終是一種 InnerBean,我們可以通過下面的調(diào)試視圖看到它的一些關(guān)鍵信息:

?通過上述分析,你可以看出最終 TimeCostFilter 實(shí)例是一種 InnerBean,所以自動(dòng)注入不到也就非常合理了。

問題修正

找到了問題的根源,解決就變得簡(jiǎn)單了。

從上述的解析中,我們可以了解到,當(dāng)使用 @WebFilter 修飾過濾器時(shí),TimeCostFilter 類型的 Bean 并沒有注冊(cè)到 Spring 容器中,真正注冊(cè)的是 FilterRegistrationBean。這里考慮到可能存在多個(gè) Filter,所以我們可以這樣修改下案例代碼:

@Controller
@Slf4j
public class StudentController {@Autowired@Qualifier("com.spring.puzzle.filter.TimeCostFilter")?FilterRegistrationBean timeCostFilter;}

這里的關(guān)鍵點(diǎn)在于:

  • 注入的類型是 FilterRegistrationBean 類型,而不是 TimeCostFilter 類型;
  • 注入的名稱是包含包名的長名稱,?即 com.spring.puzzle.filter.TimeCostFilter(不能用 TimeCostFilter),以便于存在多個(gè)過濾器時(shí)進(jìn)行精確匹配。

經(jīng)過上述修改后,代碼成功運(yùn)行無任何報(bào)錯(cuò),符合我們的預(yù)期。

案例 2:Filter 中不小心多次執(zhí)行 doFilter()

在之前的案例中,我們主要都討論了使用 @ServletComponentScan + @WebFilter 構(gòu)建過濾器過程中的一些常見問題。

而在實(shí)際生產(chǎn)過程中,如果我們需要構(gòu)建的過濾器是針對(duì)全局路徑有效,且沒有任何特殊需求(主要是指對(duì) Servlet 3.0 的一些異步特性支持),那么你完全可以直接使用 Filter 接口(或者繼承 Spring 對(duì) Filter 接口的包裝類 OncePerRequestFilter),并使用 @Component 將其包裝為 Spring 中的普通 Bean,也是可以達(dá)到預(yù)期的需求。

不過不管你使用哪一種方式,你都可能會(huì)遇到一個(gè)共同的問題:業(yè)務(wù)代碼重復(fù)執(zhí)行多次

考慮到上一個(gè)案例用的是 @ServletComponentScan + @WebFilter,這里我們不妨再以 @Component + Filter 接口的實(shí)現(xiàn)方式來呈現(xiàn)下我們的案例,也好讓你對(duì) Filter 的使用能了解到更多。

首先,還是需要通過 Spring Boot 創(chuàng)建一個(gè) Web 項(xiàng)目,不過已經(jīng)不需要

@ServletComponentScan:

@SpringBootApplication()
public class LearningApplication {public static void main(String[] args) {SpringApplication.run(LearningApplication.class, args);System.out.println("啟動(dòng)成功");}
}

StudentController 保持功能不變,所以你可以直接參考之前的代碼。另外我們定義一個(gè) DemoFilter 用來模擬問題,這個(gè) Filter 標(biāo)記了 @Component 且實(shí)現(xiàn)了 Filter 接口,已經(jīng)不同于我們上一個(gè)案例的方式:

@Component
public class DemoFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {//模擬異常System.out.println("Filter 處理中時(shí)發(fā)生異常");throw new RuntimeException();} catch (Exception e) {chain.doFilter(request, response);}chain.doFilter(request, response);}
}

全部代碼實(shí)現(xiàn)完畢,執(zhí)行后結(jié)果如下:

Filter 處理中時(shí)發(fā)生異常
......用戶注冊(cè)成功
......用戶注冊(cè)成功

這里我們可以看出,業(yè)務(wù)代碼被執(zhí)行了兩次,這并不符合我們的預(yù)期。

我們本來的設(shè)計(jì)目標(biāo)是希望 Filter 的業(yè)務(wù)執(zhí)行不會(huì)影響到核心業(yè)務(wù)的執(zhí)行,所以當(dāng)拋出異常時(shí),我們還是會(huì)調(diào)用 chain.doFilter。不過往往有時(shí)候,我們會(huì)忘記及時(shí)返回而誤入其他的 chain.doFilter,最終導(dǎo)致我們的 Filter 執(zhí)行多次。

而檢查代碼時(shí),我們往往不能立馬看出問題。所以說,這是一個(gè)典型的錯(cuò)誤,雖然原因很簡(jiǎn)單吧。不過借著這個(gè)案例,我們可以分析下為什么會(huì)執(zhí)行兩次,以深入了解 Filter 的執(zhí)行。

案例解析

在解析之前,我先給你講下 Filter 背后的機(jī)制,即責(zé)任鏈模式。

以 Tomcat 為例,我們先來看下它的 Filter 實(shí)現(xiàn)中最重要的類 ApplicationFilterChain。它采用的是責(zé)任(職責(zé))鏈設(shè)計(jì)模式,在形式上很像一種遞歸調(diào)用。

但區(qū)別在于遞歸調(diào)用是同一個(gè)對(duì)象把子任務(wù)交給同一個(gè)方法本身去完成,而職責(zé)鏈則是一個(gè)對(duì)象把子任務(wù)交給其他對(duì)象的同名方法去完成。其核心在于上下文 FilterChain 在不同對(duì)象 Filter 間的傳遞與狀態(tài)的改變,通過這種鏈?zhǔn)酱?lián),我們就可以對(duì)同一種對(duì)象資源實(shí)現(xiàn)不同業(yè)務(wù)場(chǎng)景的處理,達(dá)到業(yè)務(wù)解耦。整個(gè) FilterChain 的結(jié)構(gòu)就像這張圖一樣:

這里我們不妨還是帶著兩個(gè)問題去理解 FilterChain:

1.FilterChain 在何處被創(chuàng)建,又是在何處進(jìn)行初始化調(diào)用,從而激活責(zé)任鏈開始鏈?zhǔn)秸{(diào)用?

2.FilterChain 為什么能夠被鏈?zhǔn)秸{(diào)用,其內(nèi)在的調(diào)用細(xì)節(jié)是什么?

接下來我們直接查看負(fù)責(zé)請(qǐng)求處理的 StandardWrapperValve#invoke(),快速解決第一個(gè)問題:

public final void invoke(Request request, Response response)throws IOException, ServletException {// 省略非關(guān)鍵代碼// 創(chuàng)建filterChain ApplicationFilterChain filterChain =ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 省略非關(guān)鍵代碼 
try {if ((servlet != null) && (filterChain != null)) {// Swallow output if neededif (context.getSwallowOutput()) {// 省略非關(guān)鍵代碼 //執(zhí)行filterChainfilterChain.doFilter(request.getRequest(),response.getResponse());// 省略非關(guān)鍵代碼 }
// 省略非關(guān)鍵代碼
}

通過代碼可以看出,Spring 通過 ApplicationFilterFactory.createFilterChain() 創(chuàng)建 FilterChain,然后調(diào)用其 doFilter() 執(zhí)行責(zé)任鏈。而這些步驟的起始點(diǎn)正是 StandardWrapperValve#invoke()。

接下來,我們來一起研究第二個(gè)問題,即 FilterChain 能夠被鏈?zhǔn)秸{(diào)用的原因和內(nèi)部細(xì)節(jié)。

首先查看 ApplicationFilterFactory.createFilterChain(),來看下 FilterChain 如何被創(chuàng)建,如下所示:

public static ApplicationFilterChain createFilterChain(ServletRequest request,Wrapper wrapper, Servlet servlet) {// 省略非關(guān)鍵代碼ApplicationFilterChain filterChain = null;if (request instanceof Request) {// 省略非關(guān)鍵代碼// 創(chuàng)建Chain filterChain = new ApplicationFilterChain();// 省略非關(guān)鍵代碼}// 省略非關(guān)鍵代碼// Add the relevant path-mapped filters to this filter chainfor (int i = 0; i < filterMaps.length; i++) {// 省略非關(guān)鍵代碼ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}// 增加filterConfig到ChainfilterChain.addFilter(filterConfig);}// 省略非關(guān)鍵代碼return filterChain;
}

它創(chuàng)建 FilterChain,并將所有 Filter 逐一添加到 FilterChain 中。然后我們繼續(xù)查看 ApplicationFilterChain 類及其 addFilter():

// 省略非關(guān)鍵代碼
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private int pos = 0;
private int n = 0;
// 省略非關(guān)鍵代碼
void addFilter(ApplicationFilterConfig filterConfig) {for(ApplicationFilterConfig filter:filters)if(filter==filterConfig)return;if (n == filters.length) {ApplicationFilterConfig[] newFilters =new ApplicationFilterConfig[n + INCREMENT];System.arraycopy(filters, 0, newFilters, 0, n);filters = newFilters;}filters[n++] = filterConfig;
}

在 ApplicationFilterChain 里,聲明了 3 個(gè)變量,類型為 ApplicationFilterConfig 的數(shù)組 Filters、過濾器總數(shù)計(jì)數(shù)器 n,以及標(biāo)識(shí)運(yùn)行過程中被執(zhí)行過的過濾器個(gè)數(shù) pos。

每個(gè)被初始化的 Filter 都會(huì)通過 filterChain.addFilter(),加入到類型為 ApplicationFilterConfig 的類成員數(shù)組 Filters 中,并同時(shí)更新 Filter 總數(shù)計(jì)數(shù)器 n,使其等于 Filters 數(shù)組的長度。到這,Spring 就完成了 FilterChain 的創(chuàng)建準(zhǔn)備工作。

接下來,我們繼續(xù)看 FilterChain 的執(zhí)行細(xì)節(jié),即 ApplicationFilterChain 的 doFilter():

public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {if( Globals.IS_SECURITY_ENABLED ) {//省略非關(guān)鍵代碼internalDoFilter(request,response);//省略非關(guān)鍵代碼} else {internalDoFilter(request,response);}
}

這里邏輯被委派到了當(dāng)前類的私有方法 internalDoFilter,具體實(shí)現(xiàn)如下:

private void internalDoFilter(ServletRequest request,ServletResponse response){if (pos < n) {// pos會(huì)遞增ApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();// 省略非關(guān)鍵代碼// 執(zhí)行filterfilter.doFilter(request, response, this);// 省略非關(guān)鍵代碼} // 省略非關(guān)鍵代碼return;}// 執(zhí)行真正實(shí)際業(yè)務(wù)servlet.service(request, response);} // 省略非關(guān)鍵代碼
}

我們可以歸納下核心知識(shí)點(diǎn):

  • ApplicationFilterChain 的 internalDoFilter() 是過濾器邏輯的核心;
  • ApplicationFilterChain 的成員變量 Filters 維護(hù)了所有用戶定義的過濾器;
  • ApplicationFilterChain 的類成員變量 n 為過濾器總數(shù),變量 pos 是運(yùn)行過程中已經(jīng)執(zhí)行的過濾器個(gè)數(shù);
  • internalDoFilter() 每被調(diào)用一次,pos 變量值自增 1,即從類成員變量 Filters 中取下一個(gè) Filter;
  • filter.doFilter(request, response, this) 會(huì)調(diào)用過濾器實(shí)現(xiàn)的 doFilter(),注意第三個(gè)參數(shù)值為 this,即為當(dāng)前 ApplicationFilterChain 實(shí)例 ,這意味著:用戶需要在過濾器中顯式調(diào)用一次 javax.servlet.FilterChain#doFilter,才能完成整個(gè)鏈路;
  • pos < n 意味著執(zhí)行完所有的過濾器,才能通過 servlet.service(request, response) 去執(zhí)行真正的業(yè)務(wù)。

執(zhí)行完所有的過濾器后,代碼調(diào)用了 servlet.service(request, response) 方法。從下面這張調(diào)用棧的截圖中,可以看到,經(jīng)歷了一個(gè)很長的看似循環(huán)的調(diào)用棧,我們終于從 internalDoFilter() 執(zhí)行到了 Controller 層的 saveUser()。這個(gè)過程就不再一一細(xì)講了。

分析了這么多,最后我們?cè)賮硭伎家幌逻@個(gè)問題案例。

DemoFilter 代碼中的 doFilter() 在捕獲異常的部分執(zhí)行了一次,隨后在 try 外面又執(zhí)行了一次,因而當(dāng)拋出異常的時(shí)候,doFilter() 明顯會(huì)被執(zhí)行兩次,相對(duì)應(yīng)的 servlet.service(request, response) 方法以及對(duì)應(yīng)的 Controller 處理方法也被執(zhí)行了兩次。

你不妨回過頭再次查看上文中的過濾器執(zhí)行流程圖,相信你會(huì)有更多的收獲。

問題修正

現(xiàn)在就剩下解決這個(gè)問題了。其實(shí)只需要?jiǎng)h掉重復(fù)的 filterChain.doFilter(request, response) 就可以了,于是代碼就變成了這樣:

@Component
public class DemoFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {//模擬異常System.out.println("Filter 處理中時(shí)發(fā)生異常");throw new RuntimeException();} catch (Exception e) {//去掉下面這行調(diào)用//chain.doFilter(request, response);}chain.doFilter(request, response);}
}

?重新運(yùn)行程序和測(cè)試,結(jié)果符合預(yù)期,業(yè)務(wù)只執(zhí)行了一次?;仡欉@個(gè)問題,我想你應(yīng)該有所警示:在使用過濾器的時(shí)候,一定要注意,不管怎么調(diào)用,不能多次調(diào)用 FilterChain#doFilter()。

重點(diǎn)回顧

通過這節(jié)課的學(xué)習(xí),相信你對(duì)過濾器已經(jīng)有了一個(gè)較為深入的了解,這里我們不妨再次梳理下關(guān)鍵知識(shí)點(diǎn):

1.@WebFilter 這種方式構(gòu)建的 Filter 是無法直接根據(jù)過濾器定義類型來自動(dòng)注入的,因?yàn)檫@種 Filter 本身是以內(nèi)部 Bean 來呈現(xiàn)的,它最終是通過 FilterRegistrationBean 來呈現(xiàn)給 Spring 的。所以我們可以通過自動(dòng)注入 FilterRegistrationBean 類型來完成裝配工作,示例如下:

    @Autowired@Qualifier("com.spring.puzzle.filter.TimeCostFilter")?FilterRegistrationBean timeCostFilter;

2.我們?cè)谶^濾器的執(zhí)行中,一定要注意避免不要多次調(diào)用 doFilter(),否則可能會(huì)出現(xiàn)業(yè)務(wù)代碼執(zhí)行多次的問題。這個(gè)問題出現(xiàn)的根源往往在于“不小心”,但是要理解這個(gè)問題呈現(xiàn)的現(xiàn)象,就必須對(duì)過濾器的流程有所了解??梢钥催^濾器執(zhí)行的核心流程圖:

結(jié)合這個(gè)流程圖,我們還可以進(jìn)一步細(xì)化出以下關(guān)鍵步驟:

  • 當(dāng)一個(gè)請(qǐng)求來臨時(shí),會(huì)執(zhí)行到 StandardWrapperValve 的 invoke(),這個(gè)方法會(huì)創(chuàng)建 ApplicationFilterChain,并通過 ApplicationFilterChain#doFilter() 觸發(fā)過濾器執(zhí)行;
  • ApplicationFilterChain 的 doFilter() 會(huì)執(zhí)行其私有方法 internalDoFilter;
  • 在 internalDoFilter 方法中獲取下一個(gè) Filter,并使用 request、response、this(當(dāng)前 ApplicationFilterChain 實(shí)例)作為參數(shù)來調(diào)用 doFilter():
  • 在 Filter 類的 doFilter() 中,執(zhí)行 Filter 定義的動(dòng)作并繼續(xù)傳遞,獲取第三個(gè)參數(shù) ApplicationFilterChain,并執(zhí)行其 doFilter();
  • 此時(shí)會(huì)循環(huán)執(zhí)行進(jìn)入第 2 步、第 3 步、第 4 步,直到第 3 步中所有的 Filter 類都被執(zhí)行完畢為止;
  • 所有的 Filter 過濾器都被執(zhí)行完畢后,會(huì)執(zhí)行 servlet.service(request, response) 方法,最終調(diào)用對(duì)應(yīng)的 Controller 層方法 。

http://www.risenshineclean.com/news/2995.html

相關(guān)文章:

  • 大慶市建設(shè)大廈網(wǎng)站國家提供的免費(fèi)網(wǎng)課平臺(tái)
  • 阿里云備案做網(wǎng)站seo怎么賺錢
  • 58網(wǎng)站怎么做瀏覽度才高軟文代寫兼職
  • 云程環(huán)境建設(shè)集團(tuán)網(wǎng)站seo精準(zhǔn)培訓(xùn)課程
  • 做文明人網(wǎng)站專題百度推廣怎么做效果好
  • 廣州做網(wǎng)站西安seo陽建
  • 昆山室內(nèi)設(shè)計(jì)學(xué)校百度seo點(diǎn)擊軟件
  • 福州專業(yè)網(wǎng)站建設(shè)優(yōu)秀軟文范例800字
  • 重慶微信網(wǎng)站代理商seo提高網(wǎng)站排名
  • 網(wǎng)站開發(fā)屬于什么軟件可以免費(fèi)發(fā)外鏈的論壇
  • 國外怎么做直播網(wǎng)站蘭州seo快速優(yōu)化報(bào)價(jià)
  • 網(wǎng)站安全檢測(cè)在線武漢seo網(wǎng)站優(yōu)化
  • 北京門戶網(wǎng)站設(shè)計(jì)打開2345網(wǎng)址大全
  • 西安網(wǎng)站推廣方案建網(wǎng)站公司
  • windows7做網(wǎng)站安卓優(yōu)化大師2023
  • 網(wǎng)站如何做后臺(tái)太原seo服務(wù)
  • 用dw做網(wǎng)站時(shí)怎么添加彈窗拉新工作室在哪里接項(xiàng)目
  • 杭州做銷售去哪個(gè)網(wǎng)站好做網(wǎng)站用什么軟件
  • 沒有網(wǎng)站怎么做鏈接視頻教程5118關(guān)鍵詞工具
  • 上虞網(wǎng)站建設(shè)哪家好北京營銷推廣網(wǎng)站建設(shè)
  • wordpress網(wǎng)站亂碼關(guān)鍵詞挖掘啊愛站網(wǎng)
  • 免費(fèi)b站推廣網(wǎng)站app如何讓百度搜索排名靠前
  • 網(wǎng)站互動(dòng)交流怎么做百度中心人工電話號(hào)碼
  • 互聯(lián)網(wǎng)網(wǎng)站基礎(chǔ)中國最大網(wǎng)站排名
  • wordpress adsense主題游戲優(yōu)化大師下載安裝
  • 山東網(wǎng)站建設(shè)優(yōu)化技術(shù)太原做網(wǎng)站的工作室
  • 如何用frontpage做網(wǎng)站seo快速優(yōu)化文章排名
  • 信息分類網(wǎng)站好建嗎百度輿情
  • 南京網(wǎng)站建設(shè)王道下拉強(qiáng)獨(dú)立站seo
  • 建設(shè)銀行網(wǎng)站的支付流程太原整站優(yōu)化排名外包