Java Filter過濾器的使用教程
一、過濾器的使用以及實現(xiàn)原理
當(dāng)前的oa項目還存在什么缺陷
①對于DeptServlet、EmpServlet、OrderServlet,每一個Servlet都是處理自己相關(guān)的業(yè)務(wù);在這些Servlet執(zhí)行之前都是需要判斷用戶是否登錄了。如果用戶登錄了,可以繼續(xù)操作,如果沒有登錄,需要用戶登錄。
②這段判斷用戶是否登錄的代碼是固定的,并且在每一個Servlet類當(dāng)中都需要編寫,顯然代碼沒有得到重復(fù)利用。包括每一個Servlet都要解決中文亂碼問題,也有公共的代碼。這些代碼目前都是重復(fù)編寫,并沒有達到復(fù)用。
③怎么解決這個問題?
可以使用Servlet規(guī)范中的Filter過濾器來解決這個問題。
Filter作用與執(zhí)行原理
①Filter是過濾器。
②Filter可以在Servlet這個目標(biāo)程序執(zhí)行之前添加代碼,也可以在目標(biāo)Servlet執(zhí)行之后添加代碼;之前之后都可以添加過濾規(guī)則!
③一般情況下,都是在過濾器當(dāng)中編寫公共代碼。
過濾器怎么寫
(1)編寫一個Java類實現(xiàn)javax.servlet.Filter接口,并且實現(xiàn)這個接口當(dāng)中所有的方法;有三個方法:
①init方法:在Filter對象第一次被創(chuàng)建之后調(diào)用,并且只調(diào)用一次。
②doFilter方法:只要用戶發(fā)送一次請求,則執(zhí)行一次;發(fā)送N次請求,則執(zhí)行N次。在這個方法中編寫過濾規(guī)則!
③destroy方法:在Filter對象被釋放/銷毀之前調(diào)用,并且只調(diào)用一次。
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:MyFilter */ public class MyFilter implements Filter { public MyFilter() { System.out.println("無參數(shù)構(gòu)造方法執(zhí)行"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法執(zhí)行"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Filter1 doFilter方法開始執(zhí)行"); } @Override public void destroy() { System.out.println("destroy方法執(zhí)行"); } }
(2)在web.xml文件中對Filter進行配置,這個配置和Servlet很像。
或者使用注解:@WebFilter({"*.do"})
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>myfilter</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myfilter</filter-name> <url-pattern>/a.do</url-pattern> </filter-mapping> </web-app>
(3)打開服務(wù)器,無參構(gòu)造方法和init方法都會執(zhí)行;訪問時doFilter方法才會執(zhí)行。
①Servlet對象默認情況下,在服務(wù)器啟動的時候是不會新建對象的。
②Filter對象默認情況下,在服務(wù)器啟動的時候會新建對象,執(zhí)行無參構(gòu)造方法。
③Servlet是單例的,F(xiàn)ilter也是單例的。
(4)編寫兩個類AServlet和BServlet繼承HttpServlet,并且AServlet路徑定義為/a.do(和上面一樣),BServlet路徑定義為/b.do
package com.bjpowernode.javaweb.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:Aservlet */ @WebFilter("/a.do") public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("AServlet中的doGet方法執(zhí)行了。"); } }
BServlet類
package com.bjpowernode.javaweb.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebFilter;; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:BServlet */ @WebFilter("/b.do") public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BServlet中的doGet方法執(zhí)行了。"); } }
發(fā)送請求:http://localhost:8080/servlet14/a.do發(fā)現(xiàn)只有過濾器MyFilter類中的doFilter方法執(zhí)行了;而AServlet中的方法并沒有執(zhí)行:
(5)按理說我們發(fā)送http://localhost:8080/servlet14/a.doAservlet和MyFilter都會執(zhí)行,因為路徑都是/a.do ;實際上目標(biāo)Servlet(Aservlet)是否執(zhí)行,取決于兩個條件:
①第一:在過濾器當(dāng)中是否編寫了:chain.doFilter(request, response); 代碼。
②第二:用戶發(fā)送的請求路徑是否和Servlet的請求路徑一致。
注意:chain.doFilter(request, response); 這行代碼的作用:執(zhí)行下一個過濾器,如果下面沒有過濾器了,執(zhí)行最終的Servlet。
注意:Filter的優(yōu)先級,天生的就比Servlet優(yōu)先級高。/a.do 對應(yīng)一個Filter,也對應(yīng)一個Servlet。那么一定是先執(zhí)行Filter,然后再執(zhí)行Servlet!
所以不妨重寫一下doFilter方法,然后再次去訪問:
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:MyFilter */ public class MyFilter implements Filter { public MyFilter() { System.out.println("無參數(shù)構(gòu)造方法執(zhí)行"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法執(zhí)行"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 在請求的時候添加過濾規(guī)則。 System.out.println("Filter1 doFilter方法開始執(zhí)行"); // 執(zhí)行下一個過濾器,如果下一個不是過濾器了,則執(zhí)行目標(biāo)程序Servlet // 簡單理解就是向下走 chain.doFilter(request,response); // 在響應(yīng)的時候添加過濾規(guī)則。 System.out.println("Filter1 doFilter方法執(zhí)行結(jié)束。"); } @Override public void destroy() { System.out.println("destroy方法執(zhí)行"); } }
執(zhí)行結(jié)果如下:
三句話都執(zhí)行了,所以現(xiàn)在對于上面那個圖就更加容易理解了:
①發(fā)送請求的時候,經(jīng)過了過濾器;
②執(zhí)行chain.doFilter(request, response); 執(zhí)行下一個過濾器或者Servlet;
③進行響應(yīng)也會經(jīng)過過濾器;
補充:
①@WebFilter("/a.do") 精確匹配,只有發(fā)送a.do才會經(jīng)過這個過濾器。
②@WebFilter({"/a.do", "/b.do"}) 匹配一個數(shù)組,發(fā)送a.do和b.do都可以經(jīng)過這個過濾器。
③@WebFilter("*.do") 模糊匹配中的擴展匹配。以星號開始,注意這種路徑不要以/開始。
④@WebFilter("/dept/*")屬于前綴匹配。要以/開始。
⑤@WebFilter("/*")匹配所有的路徑。
過濾器的調(diào)用順序
過濾器的調(diào)用順序,遵循棧數(shù)據(jù)結(jié)構(gòu)。 下面通過一個例子來理解:
注意:在web.xml文件中進行配置的時候,F(xiàn)ilter的執(zhí)行順序是什么?
依靠filter-mapping標(biāo)簽的配置位置,越靠上優(yōu)先級越高。
注意:使用注解@WebFilter的時候,F(xiàn)ilter的執(zhí)行順序是怎樣的呢?
執(zhí)行順序是:比較Filter這個類名。
比如:FilterA和FilterB,則先執(zhí)行FilterA
比如:Filter1和Filter2,則先執(zhí)行Filter1
定義一個MyFilter1過濾器
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; public class MyFilter1 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法執(zhí)行"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Filter1 doFilter方法開始執(zhí)行"); chain.doFilter(request,response); System.out.println("Filter1 doFilter方法執(zhí)行結(jié)束。"); } @Override public void destroy() { System.out.println("destroy方法執(zhí)行"); } }
定義一個MyFilter2過濾器
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; public class MyFilter2 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Filter2 doFilter begin"); chain.doFilter(request, response); System.out.println("Filter2 doFilter end"); } @Override public void destroy() { } }
選擇在xml文件中配置,對應(yīng)的web.xml文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>myfilter</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter1</filter-class> </filter> <filter-mapping> <filter-name>myfilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter> <filter-name>myfilter2</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter2</filter-class> </filter> <filter-mapping> <filter-name>myfilter2</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> </web-app>
定義一個AServlet類,路徑通過注解的方式
package com.bjpowernode.javaweb.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/a.do") public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("AServlet中的doGet方法執(zhí)行了。"); } }
Filter的優(yōu)先級是比Servlet高的,所以當(dāng)進行訪問時:http://localhost:8080/servlet14/a.do會先按照web.xml文件中的filter-mapping標(biāo)簽位置進行訪問,遇到chain.doFilter(request, response)就會跳轉(zhuǎn)到下一個Filter或者Servlet;所以最終結(jié)果如下:
Filter的生命周期
①Filter的生命周期和Servlet對象生命周期一致。
②唯一的區(qū)別:Filter默認情況下,在服務(wù)器啟動階段就實例化;而Servlet不會。
二、責(zé)任鏈設(shè)計模式改造oa項目
(1)我們先看一段java代碼;這段代碼也能實現(xiàn)過濾器的效果,遵循棧數(shù)據(jù)結(jié)構(gòu)。但是這個程序的問題:在編譯階段已經(jīng)完全確定了調(diào)用關(guān)系。 如果你想改變他們的調(diào)用順序,必須修改以下java源代碼。java代碼修改,需要重新編譯,項目需要重新測試,項目需要重新發(fā)布。這是一個繁瑣的過程。顯然,這種設(shè)計違背了:OCP原則。(開閉原則)
package com.bjpowernode.javaweb.servlet; public class Test { public static void main(String[] args) { System.out.println("main begin"); m1(); System.out.println("main over"); } private static void m1() { System.out.println("m1 begin"); m2(); System.out.println("m1 over"); } private static void m2() { System.out.println("m2 begin"); m3(); System.out.println("m2 over"); } private static void m3() { System.out.println("目標(biāo)正在執(zhí)行中。。。。"); } }
(2)Filter過濾器里有一個設(shè)計模式 :責(zé)任鏈設(shè)計模式。
過濾器最大的優(yōu)點: 在程序編譯階段不會確定調(diào)用順序。因為Filter的調(diào)用順序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的順序就可以調(diào)整Filter的執(zhí)行順序。顯然Filter的執(zhí)行順序是在程序運行階段動態(tài)組合的。那么這種設(shè)計模式被稱為責(zé)任鏈設(shè)計模式。
注:責(zé)任鏈設(shè)計模式最大的核心思想:在程序運行階段,動態(tài)的組合程序的調(diào)用順序!
tip:對于過濾器Filter的配置一般是配置到web.xml文件當(dāng)中,不要使用注解的方式;這樣更加的容易修改它們的執(zhí)行順序。
(3)使用過濾器改造OA項目
①首先配置web.xml文件
<filter> <filter-name>loginfilter</filter-name> <filter-class>com.bjpowernode.oa.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>loginfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
②編寫一個過濾器,作為判斷的登錄
目前寫的路徑是:/* 表示所有的請求均攔截,所以要先分析一下,什么情況下不能攔截?
??用戶訪問 index.jsp的時候不能攔截
??用戶已經(jīng)登錄了,這個需要放行,不能攔截。
??用戶要去登錄,這個也不能攔截。
??WelcomeServlet也不能攔截。
package com.bjpowernode.oa.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; /** * @Package:com.bjpowernode.oa.filter * @Project:JavaWeb * @name:LoginFilter */ public class LoginFilter implements Filter { // 也可以只重寫其中一個方法 @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // 注意這里要先進行強轉(zhuǎn)為HttpServlet,后面才能調(diào)用對應(yīng)的方法 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse) resp; // 獲取請求路徑 String servletPath = request.getServletPath(); // 獲取當(dāng)前session對象,獲取不到就返回null HttpSession session = request.getSession(false); if("/index.jsp".equals(servletPath) || "/welcome".equals(servletPath) || "/dept/login".equals(servletPath) || "/dept/exit".equals(servletPath) || (session != null && session.getAttribute("username") != null)){ // 經(jīng)過上面過濾,只有上面的路徑才會直接通過;其它的操作都要先跳轉(zhuǎn)到登錄頁面 // 繼續(xù)往下走 chain.doFilter(request, response); }else{ response.sendRedirect(request.getContextPath() + "/index.jsp"); } } }
這個過濾器寫好以后,以后其它類也可以調(diào)用這個過濾器,都不用驗證登錄了;例如:一個員工的Servlet(EmpServlet)
package com.bjpowernode.oa.web.action; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.io.IOException; /** * 員工管理的。 * 員工管理的前提也是需要先登錄。 */ public class EmpServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 亂碼問題也可以寫入過濾器當(dāng)中 // post請求亂碼問題 request.setCharacterEncoding("UTF-8"); // 響應(yīng)中文亂碼問題 response.setContentType("text/html;charset=UTF-8"); HttpSession session = request.getSession(false); if(session != null && session.getAttribute("username") != null){ String servletPath = request.getServletPath(); //... }else{ response.sendRedirect(request.getContextPath() + "/index.jsp"); } } }
對于原來的DeptServlet類也可以進行修改了:
HttpSession session = request.getSession(false); if (session != null && session.getAttribute("username") != null){ // session對象不一定為null // 獲取servlet path String servletPath = request.getServletPath(); if ("/dept/list".equals(servletPath)){ doList(request,response); }else if("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if("/dept/delete".equals(servletPath)) { doDel(request, response); }else if("/dept/add".equals(servletPath)) { doAdd(request, response); }else if("/dept/modify".equals(servletPath)) { doModify(request, response); } }else{ // 跳轉(zhuǎn)到登錄頁面 response.sendRedirect(request.getContextPath()+"/index.jsp"); }
修改為:
String servletPath = request.getServletPath(); if ("/dept/list".equals(servletPath)){ doList(request,response); }else if("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if("/dept/delete".equals(servletPath)) { doDel(request, response); }else if("/dept/add".equals(servletPath)) { doAdd(request, response); }else if("/dept/modify".equals(servletPath)) { doModify(request, response); }
到此這篇關(guān)于Java Filter過濾器的使用教程的文章就介紹到這了,更多相關(guān)Java Filter過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端存token后端獲取token代碼實例(Spring?Boot)
Token其實就是訪問資源的憑證,一般是用戶通過用戶名和密碼登錄成功之后,服務(wù)器將登陸憑證做數(shù)字簽名,加密之后得到的字符串作為token,這篇文章主要給大家介紹了關(guān)于前端存token,Spring?Boot后端獲取token的相關(guān)資料,需要的朋友可以參考下2024-07-07如何在Spring?Boot框架中使用攔截器實現(xiàn)URL限制
在Spring?Boot框架中,您可以使用攔截器(Interceptor)來控制限制URL列表,本文通過一個簡單的示例給大家介紹Spring?Boot?攔截器實現(xiàn)URL限制的操作方法,感興趣的朋友跟隨小編一起看看吧2023-08-08詳解Springboot整合ActiveMQ(Queue和Topic兩種模式)
這篇文章主要介紹了詳解Springboot整合ActiveMQ(Queue和Topic兩種模式),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04SpringMVC接收復(fù)雜集合對象(參數(shù))代碼示例
這篇文章主要介紹了SpringMVC接收復(fù)雜集合對象(參數(shù))代碼示例,舉接收List<String>、List<User>、List<Map<String,Object>>、User[]、User(bean里面包含List)幾種較為復(fù)雜的集合參數(shù),具有一定參考價值,需要的朋友可以了解下。2017-11-11Springcloud實現(xiàn)服務(wù)多版本控制的示例代碼
這篇文章主要介紹了Springcloud實現(xiàn)服務(wù)多版本控制的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05