Servlet Filter過濾器執(zhí)行順序
Servlet中的過濾器相當(dāng)于守護(hù)后臺(tái)資源的一道關(guān)卡,我們可以在過濾器中進(jìn)行身份校驗(yàn)、權(quán)限認(rèn)證、請(qǐng)求過濾等。
過濾器本身并不難,我們只需要知道他的定義方法、作用范圍、執(zhí)行順序即可。
網(wǎng)上對(duì)于過濾器執(zhí)行順序的描述可能會(huì)讓人產(chǎn)生誤解。
圖片來源于網(wǎng)絡(luò)

客戶端請(qǐng)求到達(dá)的時(shí)候,經(jīng)過一次過濾器。
服務(wù)器處理完請(qǐng)求的時(shí)候,經(jīng)過一次過濾器。
雖然經(jīng)過兩次過濾器,但不代表同樣的代碼執(zhí)行了兩次。
下面做了個(gè)簡(jiǎn)單的測(cè)試,看下執(zhí)行結(jié)果就應(yīng)該知道真正的執(zhí)行流程了。
測(cè)試環(huán)境
tomcat9(servlet4.0)
jdk1.8
新版servlet可以通過注解注冊(cè)servlet組件以及過濾器,無需再到web.xml下注冊(cè)了。
測(cè)試過程
測(cè)試之間要先知道filterChain(過濾鏈)是干嘛的。
一個(gè)過濾器處理完后,會(huì)把request和response對(duì)象通過filterchain傳遞給下一個(gè)過濾器,如果沒有下一個(gè)過濾器,則會(huì)直接開始執(zhí)行業(yè)務(wù)代碼,
單個(gè)過濾器
定義一個(gè)過濾器A
@WebFilter(value = "/*", filterName="A")
public class FilterA implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(new Date()));
System.out.println("A:攔截1");
chain.doFilter(request, response);
System.out.println(format.format(new Date()));
System.out.println("A:攔截2");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
定義一個(gè)servlet,sleep5秒
@WebServlet("/mainUrl")
public class MainController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MainController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
運(yùn)行結(jié)果
2020-12-01 10:46:50 A:攔截1 2020-12-01 10:46:55 A:攔截2
執(zhí)行順序:
filterChain之前的代碼 ——>業(yè)務(wù)處理——>filterChain之后的代碼。
多個(gè)過濾器
servlet的注解在多個(gè)過濾器的情況下,是按照過濾器的名稱來排序的,例如A開頭的過濾器,在B開頭的后面。
A過濾器
@WebFilter(value = "/*", filterName="A")
public class FilterA implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(new Date()));
System.out.println("A:攔截1");
chain.doFilter(request, response);
System.out.println(format.format(new Date()));
System.out.println("A:攔截2");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
B過濾器
@WebFilter(value = "/*", filterName="B")
public class FilterB implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(new Date()));
System.out.println("B:攔截1");
chain.doFilter(request, response);
System.out.println(format.format(new Date()));
response.setContentType("normal content");
System.out.println("B:攔截2");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
運(yùn)行結(jié)果:
2020-12-01 10:53:00 A:攔截1 2020-12-01 10:53:00 B:攔截1 2020-12-01 10:53:05 B:攔截2 2020-12-01 10:53:05 A:攔截2
執(zhí)行順序:
B:攔截1和B:攔截2之間,停頓了5秒處理業(yè)務(wù)。所以先執(zhí)行了 chain.doFilter前的 A、B過濾器代碼,處理完業(yè)務(wù)返回的時(shí)候正好相反,先返回執(zhí)行B的代碼,再執(zhí)行的A的代碼。
總結(jié)
再來看這個(gè)圖,可以略微改一下了。

分界線是filterChain過濾鏈,請(qǐng)求進(jìn)來的時(shí)候,執(zhí)行filterchain之前的代碼,返回response的時(shí)候,執(zhí)行filterchain之后的代碼。
多個(gè)過濾器之間的執(zhí)行順序,滿足“先進(jìn)后出” (棧結(jié)構(gòu))的原則。
其他
如果在測(cè)試過程中,發(fā)現(xiàn)過濾器執(zhí)行了很多次,那么也可能是因?yàn)闇y(cè)試環(huán)境中包含了某些靜態(tài)資源。
過濾器A
@WebFilter(value = "/*", filterName="A")
public class FilterA implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(new Date()));
System.out.println("A:攔截1");
chain.doFilter(request, response);
System.out.println(format.format(new Date()));
System.out.println("A:攔截2");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
過濾器B
@WebFilter(value = "/*", filterName="B")
public class FilterB implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest) request;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(req.getRequestURL());
System.out.println(format.format(new Date()));
System.out.println("B:攔截1");
chain.doFilter(request, response);
System.out.println(format.format(new Date()));
response.setContentType("normal content");
System.out.println("B:攔截2");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
主程序
@WebServlet("/mainUrl")
public class MainController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MainController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
request.getRequestDispatcher("/WEB-INF/pages/main.jsp").forward(request, response);
// response.sendRedirect("/WEB-INF/pages/main.jsp");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
執(zhí)行結(jié)果:
2020-12-01 11:09:38 A:攔截1 http://localhost:8080/StudentManage/mainUrl 2020-12-01 11:09:38 B:攔截1 2020-12-01 11:09:43 B:攔截2 2020-12-01 11:09:43 A:攔截2 2020-12-01 11:09:44 A:攔截1 http://localhost:8080/StudentManage/css/bootstrap.css.map 2020-12-01 11:09:44 B:攔截1 2020-12-01 11:09:44 B:攔截2 2020-12-01 11:09:44 A:攔截2
轉(zhuǎn)發(fā)(forward)的頁面中需要請(qǐng)求靜態(tài)資源,再次觸發(fā)了過濾器。
以上就是Servlet Filter過濾器執(zhí)行順序的詳細(xì)內(nèi)容,更多關(guān)于Servlet Filter過濾器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 淺談SpringMVC的攔截器(Interceptor)和Servlet 的過濾器(Filter)的區(qū)別與聯(lián)系 及SpringMVC 的配置文件
- JavaWeb Servlet中Filter過濾器的詳解
- servlet過濾器(Filter)詳解(九)
- 詳解Servlet之過濾器(Filter)
- Spring Boot使用Servlet及Filter過程詳解
- springboot掃描自定義的servlet和filter代碼詳解
- SpringBoot初始教程之Servlet、Filter、Listener配置詳解
- 詳談Servlet和Filter的區(qū)別以及兩者在Struts2和Springmvc中的應(yīng)用
- web.xml中servlet, bean, filter, listenr 加載順序_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- Filter、Servlet、Listener的學(xué)習(xí)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- Spring Boot 編寫Servlet、Filter、Listener、Interceptor的方法
- 解決cannot be cast to javax.servlet.Filter 報(bào)錯(cuò)的問題
相關(guān)文章
springmvc path請(qǐng)求映射到bean 方法的流程
這篇文章主要介紹了springmvc path請(qǐng)求映射到bean 方法的流程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07
使用IDEA創(chuàng)建SpringBoot項(xiàng)目的方法步驟
這篇文章主要介紹了使用IDEA創(chuàng)建SpringBoot項(xiàng)目的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Java實(shí)現(xiàn)簡(jiǎn)單文件過濾器功能
下面小編就為大家分享一篇Java實(shí)現(xiàn)簡(jiǎn)單文件過濾器功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
Springboot讀取templates文件html代碼實(shí)例
這篇文章主要介紹了Springboot讀取templates文件html代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Java8中字符串處理庫(kù)strman-java的使用示例
除了Java本身的字符串處理方式外,我們還可以使用Apache Common Langs里的StringUtils來簡(jiǎn)化String的操作。但以上兩種方式對(duì)于我們?nèi)粘>幊讨凶钊菀着龅降淖址幚韥碚f,仍然顯得有些不足。所以這篇文章給大家介紹Java8中字符串處理庫(kù)strman-java的使用。2016-09-09
在SpringBoot中實(shí)現(xiàn)線程池并行處理任務(wù)的方法詳解
在使用Spring Boot開發(fā)應(yīng)用程序時(shí),我們經(jīng)常需要處理一些耗時(shí)的任務(wù),例如網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)操作或者其他需要花費(fèi)一定時(shí)間的計(jì)算任務(wù),本文將介紹如何在Spring Boot中使用線程池來實(shí)現(xiàn)任務(wù)的并行處理2023-06-06
java開發(fā)ShardingSphere的路由引擎類型示例詳解
這篇文章主要為大家介紹了java開發(fā)ShardingSphere的路由引擎類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

