Java Filter過濾器的使用教程
一、過濾器的使用以及實現(xiàn)原理
當前的oa項目還存在什么缺陷
①對于DeptServlet、EmpServlet、OrderServlet,每一個Servlet都是處理自己相關的業(yè)務;在這些Servlet執(zhí)行之前都是需要判斷用戶是否登錄了。如果用戶登錄了,可以繼續(xù)操作,如果沒有登錄,需要用戶登錄。
②這段判斷用戶是否登錄的代碼是固定的,并且在每一個Servlet類當中都需要編寫,顯然代碼沒有得到重復利用。包括每一個Servlet都要解決中文亂碼問題,也有公共的代碼。這些代碼目前都是重復編寫,并沒有達到復用。
③怎么解決這個問題?
可以使用Servlet規(guī)范中的Filter過濾器來解決這個問題。
Filter作用與執(zhí)行原理
①Filter是過濾器。
②Filter可以在Servlet這個目標程序執(zhí)行之前添加代碼,也可以在目標Servlet執(zhí)行之后添加代碼;之前之后都可以添加過濾規(guī)則!
③一般情況下,都是在過濾器當中編寫公共代碼。

過濾器怎么寫
(1)編寫一個Java類實現(xiàn)javax.servlet.Filter接口,并且實現(xiàn)這個接口當中所有的方法;有三個方法:
①init方法:在Filter對象第一次被創(chuàng)建之后調用,并且只調用一次。
②doFilter方法:只要用戶發(fā)送一次請求,則執(zhí)行一次;發(fā)送N次請求,則執(zhí)行N次。在這個方法中編寫過濾規(guī)則!
③destroy方法:在Filter對象被釋放/銷毀之前調用,并且只調用一次。
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ù)構造方法執(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)打開服務器,無參構造方法和init方法都會執(zhí)行;訪問時doFilter方法才會執(zhí)行。
①Servlet對象默認情況下,在服務器啟動的時候是不會新建對象的。
②Filter對象默認情況下,在服務器啟動的時候會新建對象,執(zhí)行無參構造方法。
③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 ;實際上目標Servlet(Aservlet)是否執(zhí)行,取決于兩個條件:
①第一:在過濾器當中是否編寫了:chain.doFilter(request, response); 代碼。
②第二:用戶發(fā)送的請求路徑是否和Servlet的請求路徑一致。
注意:chain.doFilter(request, response); 這行代碼的作用:執(zhí)行下一個過濾器,如果下面沒有過濾器了,執(zhí)行最終的Servlet。
注意:Filter的優(yōu)先級,天生的就比Servlet優(yōu)先級高。/a.do 對應一個Filter,也對應一個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ù)構造方法執(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í)行目標程序Servlet
// 簡單理解就是向下走
chain.doFilter(request,response);
// 在響應的時候添加過濾規(guī)則。
System.out.println("Filter1 doFilter方法執(zhí)行結束。");
}
@Override
public void destroy() {
System.out.println("destroy方法執(zhí)行");
}
}執(zhí)行結果如下:
三句話都執(zhí)行了,所以現(xiàn)在對于上面那個圖就更加容易理解了:
①發(fā)送請求的時候,經(jīng)過了過濾器;
②執(zhí)行chain.doFilter(request, response); 執(zhí)行下一個過濾器或者Servlet;
③進行響應也會經(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("/*")匹配所有的路徑。
過濾器的調用順序
過濾器的調用順序,遵循棧數(shù)據(jù)結構。 下面通過一個例子來理解:
注意:在web.xml文件中進行配置的時候,F(xiàn)ilter的執(zhí)行順序是什么?
依靠filter-mapping標簽的配置位置,越靠上優(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í)行結束。");
}
@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文件中配置,對應的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高的,所以當進行訪問時:http://localhost:8080/servlet14/a.do會先按照web.xml文件中的filter-mapping標簽位置進行訪問,遇到chain.doFilter(request, response)就會跳轉到下一個Filter或者Servlet;所以最終結果如下:

Filter的生命周期
①Filter的生命周期和Servlet對象生命周期一致。
②唯一的區(qū)別:Filter默認情況下,在服務器啟動階段就實例化;而Servlet不會。
二、責任鏈設計模式改造oa項目
(1)我們先看一段java代碼;這段代碼也能實現(xiàn)過濾器的效果,遵循棧數(shù)據(jù)結構。但是這個程序的問題:在編譯階段已經(jīng)完全確定了調用關系。 如果你想改變他們的調用順序,必須修改以下java源代碼。java代碼修改,需要重新編譯,項目需要重新測試,項目需要重新發(fā)布。這是一個繁瑣的過程。顯然,這種設計違背了: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("目標正在執(zhí)行中。。。。");
}
}(2)Filter過濾器里有一個設計模式 :責任鏈設計模式。
過濾器最大的優(yōu)點: 在程序編譯階段不會確定調用順序。因為Filter的調用順序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的順序就可以調整Filter的執(zhí)行順序。顯然Filter的執(zhí)行順序是在程序運行階段動態(tài)組合的。那么這種設計模式被稱為責任鏈設計模式。
注:責任鏈設計模式最大的核心思想:在程序運行階段,動態(tài)的組合程序的調用順序!
tip:對于過濾器Filter的配置一般是配置到web.xml文件當中,不要使用注解的方式;這樣更加的容易修改它們的執(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 {
// 注意這里要先進行強轉為HttpServlet,后面才能調用對應的方法
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse) resp;
// 獲取請求路徑
String servletPath = request.getServletPath();
// 獲取當前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)過上面過濾,只有上面的路徑才會直接通過;其它的操作都要先跳轉到登錄頁面
// 繼續(xù)往下走
chain.doFilter(request, response);
}else{
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
}這個過濾器寫好以后,以后其它類也可以調用這個過濾器,都不用驗證登錄了;例如:一個員工的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 {
// 亂碼問題也可以寫入過濾器當中
// post請求亂碼問題
request.setCharacterEncoding("UTF-8");
// 響應中文亂碼問題
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{
// 跳轉到登錄頁面
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);
}到此這篇關于Java Filter過濾器的使用教程的文章就介紹到這了,更多相關Java Filter過濾器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
前端存token后端獲取token代碼實例(Spring?Boot)
Token其實就是訪問資源的憑證,一般是用戶通過用戶名和密碼登錄成功之后,服務器將登陸憑證做數(shù)字簽名,加密之后得到的字符串作為token,這篇文章主要給大家介紹了關于前端存token,Spring?Boot后端獲取token的相關資料,需要的朋友可以參考下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兩種模式),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
SpringMVC接收復雜集合對象(參數(shù))代碼示例
這篇文章主要介紹了SpringMVC接收復雜集合對象(參數(shù))代碼示例,舉接收List<String>、List<User>、List<Map<String,Object>>、User[]、User(bean里面包含List)幾種較為復雜的集合參數(shù),具有一定參考價值,需要的朋友可以了解下。2017-11-11
Springcloud實現(xiàn)服務多版本控制的示例代碼
這篇文章主要介紹了Springcloud實現(xiàn)服務多版本控制的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05

