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