Servlet Filter過濾器執行順序


為什么要用過濾器?

Servlet中的過濾器相當於守護后台資源的一道關卡,我們可以在過濾器中進行身份校驗、權限認證、請求過濾等。

過濾器本身並不難,我們只需要知道他的定義方法、作用范圍、執行順序即可。

網上對於過濾器執行順序的描述可能會讓人產生誤解。

圖片來源於網絡

 

 

 客戶端請求到達的時候,經過一次過濾器。

服務器處理完請求的時候,經過一次過濾器。

雖然經過兩次過濾器,但不代表同樣的代碼執行了兩次。

下面做了個簡單的測試,看下執行結果就應該知道真正的執行流程了。

測試環境

tomcat9(servlet4.0)

jdk1.8

新版servlet可以通過注解注冊servlet組件以及過濾器,無需再到web.xml下注冊了。

測試過程

測試之間要先知道filterChain(過濾鏈)是干嘛的。

一個過濾器處理完后,會把request和response對象通過filterchain傳遞給下一個過濾器,如果沒有下一個過濾器,則會直接開始執行業務代碼,

單個過濾器

定義一個過濾器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() {

    }

}

定義一個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);
    }

}

運行結果

2020-12-01 10:46:50
A:攔截1
2020-12-01 10:46:55
A:攔截2

執行順序:

filterChain之前的代碼 ——>業務處理——>filterChain之后的代碼。

多個過濾器

servlet的注解在多個過濾器的情況下,是按照過濾器的名稱來排序的,例如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() {

    }
}

運行結果:

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

執行順序:

B:攔截1和B:攔截2之間,停頓了5秒處理業務。所以先執行了  chain.doFilter前的 A、B過濾器代碼,處理完業務返回的時候正好相反,先返回執行B的代碼,再執行的A的代碼。

總結

再來看這個圖,可以略微改一下了。

 

 

 分界線是filterChain過濾鏈,請求進來的時候,執行filterchain之前的代碼,返回response的時候,執行filterchain之后的代碼。

多個過濾器之間的執行順序,滿足“先進后出” (棧結構)的原則。

其他

如果在測試過程中,發現過濾器執行了很多次,那么也可能是因為測試環境中包含了某些靜態資源。

過濾器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);
    }

}

執行結果:

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

轉發(forward)的頁面中需要請求靜態資源,再次觸發了過濾器。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM