JavaWeb三大組件之一的Filter詳解
1. 概念
Filter過濾器是JavaWeb的三大組件之一。三大組件:Servlet,Listener,F(xiàn)ilter
Filter過濾器是JavaEE的規(guī)范,即接口
作用:攔截請求,過濾響應(yīng)
攔截請求常見的應(yīng)用場景:
- 權(quán)限檢查
- 日記操作
- 事務(wù)管理…
2. 基本使用
例子:要求在你的web工程下,有一個admin目錄,這個目錄下的所有資源(html,jpg,jsp等)都必須是用戶登錄之后才允許訪問。
- 根據(jù)之前我們學(xué)的,用戶登陸后我們把用戶信息保存到Session域中,所以我們判斷Session中是否包含有用戶信息即可,但這種方案只能用在jsp頁面中
- 使用Filter,可以使用在任何資源上(Filter在獲取目標(biāo)資源前執(zhí)行)
使用:
寫個類去實現(xiàn)Filter接口
// 先寫個類去實現(xiàn)javax.servlet.Filter public class AdminFilter implements Filter { // 這個方法重要,主要用于攔截請求??!(權(quán)限檢查) @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; // 要強(qiáng)轉(zhuǎn)一下,才能獲取Session HttpServletResponse resp = (HttpServletResponse) servletResponse; HttpSession session = req.getSession(); // 獲取Session Object user = session.getAttribute("user"); if (user == null) { req.getRequestDispatcher("/login.jsp").forward(req, resp); //沒登錄轉(zhuǎn)發(fā)到登陸頁面 return; } else { // 如果已經(jīng)登錄,如果有下一個Filter則進(jìn)入,沒有則放行,去訪問用戶請求的資源!(沒有這行是不行的) filterChain.doFilter(req, resp); } } // 下面兩個可以空實現(xiàn) @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
xml配置過濾器(和Servlet差不多)
<!-- 配置過濾器--> <filter> <filter-name>AdminFilter</filter-name> <!-- 給Filter起一個名稱--> <filter-class>com.sutong.filter.AdminFilter</filter-class> <!-- 全類名--> </filter> <filter-mapping> <filter-name>AdminFilter</filter-name> <!-- 表示當(dāng)前的攔截路徑給哪個Filter使用--> <!-- 攔截路徑,斜杠表示到工程路徑,映射到web目錄,admin/* 表示admin目錄下全部資源--> <url-pattern>/admin/*</url-pattern> </filter-mapping>
3. 生命周期
Filter 的方法執(zhí)行順序:
- 構(gòu)造器 方法
init()
初始化方法, 第一二步在web工程啟動的時候已經(jīng)執(zhí)行(即Filter已經(jīng)創(chuàng)建)doFilter()
,每次攔截到請求就會執(zhí)行destroy()
,停止web的時候執(zhí)行
4. FilterConfig類
FIlterConfig,F(xiàn)ilter過濾器的配置文件類,Tomcat每次創(chuàng)建的Filter的時候,會同時創(chuàng)建一個FilterConfig類。
作用:獲取Filter過濾器的配置內(nèi)容
- 獲取Filter的名稱,即配置文件中
filter-name
標(biāo)簽里面的內(nèi)容 - 獲取Filter在web.xml中配置的
init-param
初始化參數(shù) (在filter標(biāo)簽里面配置初始化參數(shù),和Servlet一樣) - 獲取
ServletContext
對象
@Override public void init(FilterConfig filterConfig) throws ServletException { String filterName = filterConfig.getFilterName(); // 1 String value = filterConfig.getInitParameter("key1"); // 2 ServletContext servletContext = filterConfig.getServletContext(); // 3 }
5. FilterChain類
FilterChain 是過濾器鏈(多個過濾器一起工作)
filterChain.doFilter(req, resp)
該方法是作用:
- 執(zhí)行下一個Filter(如果有)
- 執(zhí)行目標(biāo)資源(沒有了Filter了)
如果驗證通過,執(zhí)行完目標(biāo)資源后會返回 doFilter 方法調(diào)用的地方,繼續(xù)執(zhí)行下面的代碼。
當(dāng)多個過濾器時,攔截同一個文件/目錄時,F(xiàn)ilter 配置文件的順序(即web.xml中的配置順序)決定了每 Filter 的執(zhí)行順序,配置在前先執(zhí)行。
當(dāng)多個過濾器的特點:?
- 所有的 Filter 和目標(biāo)資源默認(rèn)都執(zhí)行在同一個線程中
- 多個 Filter 共同執(zhí)行的時候他們都使用一個 Request 對象
6. 攔截路徑
精確匹配
<url-pattern>/target.jsp</url-pattern>
--> http://ip:port/工程路徑/target.jsp
目錄匹配
<url-pattern>/admin/*</url-pattern>
-> http://ip:port/工程路徑/admin/*
后綴名匹配
<url-pattern>*.html</url-pattern>
-> 表示要攔截的地址必須以 .html結(jié)尾
這個后綴名不一定是現(xiàn)有文件的后綴名,是個字符串就行 *.abc
也行。注意不是以斜杠開頭
Filter只關(guān)系請求的地址是否匹配,不關(guān)心資源是否存在。
7. ThreadLocal+Filter管理事務(wù)
7.1 ThreadLocal
ThreadLocal是jdk1.2開始的,作用:可以解決多線程的數(shù)據(jù)安全問題
ThreadLocal可以給當(dāng)前線程關(guān)聯(lián)一個數(shù)據(jù)(可以是普通變量,對象,集合等)
(可以簡單理解為,像Map一樣,當(dāng)前線程名為key,關(guān)聯(lián)的數(shù)據(jù)為value)
如果想要給當(dāng)前線程關(guān)聯(lián)多個數(shù)據(jù)則需要多個ThreadLocal實例,ThreadLocal實例一般都是 static 類型,其中保存的數(shù)據(jù)在線程銷毀后由JVM虛擬機(jī)自動釋放。
// Hashtable線程安全 public static Map<String, Object> map = new Hashtable<>(); // ThreadLocal泛型就是關(guān)聯(lián)數(shù)據(jù)的類型,類似:Map中V的類型,K是當(dāng)前線程 // 只能關(guān)聯(lián)一個數(shù)據(jù),多個則需new多個 public static ThreadLocal<Object> threadLocal = new ThreadLocal<>(); // 使用 public void test() { map.put(Thread.currentThread().getName(), "Map存數(shù)據(jù)"); threadLocal.set("ThreadLocal存數(shù)據(jù)"); // 取數(shù)據(jù) Object obj1 = map.get(Thread.currentThread().getName()); Object obj2 = threadLocal.get(); }
7.2 MySQL事務(wù)前提
我們在book項目的時候,如果生成訂單后發(fā)生錯誤,則生成訂單成功而訂單詳情生成失敗,這是嚴(yán)重錯誤的,所以我們要使用事務(wù)確保這些操作在一個事務(wù)內(nèi)。
而確保在一個事務(wù)的前期是 使用同一個 Connection 連接對象! 這里就可以用 ThreadLocal
了,把Connection
存到ThreadLocal
中,確保多個Service使用的是同一個連接對象。
而使用 ThreadLocal
關(guān)聯(lián)數(shù)據(jù)要確保上面這些操作在一個線程中執(zhí)行! (經(jīng)過驗證我們book中生成訂單操作都是在一個線程下的)
String orderId = null; try { orderId = orderService.createOrder(cart, loginUser.getId()); JdbcUtils.commitAndClose(); // 生成訂單,沒異常提交事務(wù)關(guān)閉連接 } catch (Exception e) { JdbcUtils.rollbackAndClose(); // 有異?;貪L,關(guān)閉連接 ??! } // 但這樣做每個xxxService.xxx() 都要進(jìn)行try catch,太麻煩了,可以使用Filter,看下面
7.3 Filter統(tǒng)一管理
使用Filter統(tǒng)一給所有的 Service 方法都加上 try-catch,來實現(xiàn)管理??!
所有的異常都要拋給 Filter,不要私自處理異常
TransactionFilter.java :
public class TransactionFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { try { // 下面這行相當(dāng)于調(diào)用xxxService.xxx()方法, // 所以我們對這行進(jìn)行try-catch就行了(即給所有的Servlet中的所以方法進(jìn)行了try-catch) filterChain.doFilter(servletRequest, servletResponse); JdbcUtils.commitAndClose(); // 沒異常提交事務(wù),關(guān)閉連接 } catch (Exception e) { JdbcUtils.rollbackAndClose(); // 有異?;貪L,關(guān)閉連接 e.printStackTrace(); // 可以不打印,打印可以讓我們開到什么錯誤 throw new RuntimeException(e); // 再把錯誤拋給Tomcat去顯示我們準(zhǔn)備的錯誤頁面?。。。。。? // (如果有異常了,用戶頁面則會一頁空白,我們要給用戶一點友好的提示!可以交給Tomcat展示友好的錯誤頁面信息) } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
web.xml
<filter> <filter-name>TransactionFilter</filter-name> <filter-class>com.sutong.filter.TransactionFilter</filter-class> </filter> <filter-mapping> <filter-name>TransactionFilter</filter-name> <url-pattern>/*</url-pattern> <!-- /* 代表當(dāng)前工程的所有請求,相當(dāng)于對工程下的所有請求進(jìn)行了try-catch--> </filter-mapping>
7.4 錯誤信息展示
TransactionFilter.java
try { filterChain.doFilter(servletRequest, servletResponse); JdbcUtils.commitAndClose(); } catch (Exception e) { JdbcUtils.rollbackAndClose(); throw new RuntimeException(e); // 再把錯誤拋給Tomcat去顯示我們準(zhǔn)備的錯誤頁面?。。。。?! // (如果有異常了,用戶頁面則會一頁空白,我們要給用戶一點友好的提示!可以交給Tomcat展示友好的錯誤頁面信息) }
web.xml配置
<!-- error-page配置服務(wù)器出錯后,自動跳轉(zhuǎn)的頁面--> <error-page> <error-code>500</error-code> <!-- 錯誤類型--> <location>/pages/error/error500.jsp</location> <!-- 要跳轉(zhuǎn)去的頁面路徑--> </error-page> <error-page> <error-code>404</error-code> <location>/pages/error/error404.jsp</location> </error-page>
到此這篇關(guān)于JavaWeb三大組件之一的Filter詳解的文章就介紹到這了,更多相關(guān)JavaWeb Filter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Socket實現(xiàn)猜數(shù)字小游戲
這篇文章主要為大家詳細(xì)介紹了Java Socket實現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-09-09使用jekins自動構(gòu)建部署java maven項目的方法步驟
這篇文章主要介紹了使用jekins自動構(gòu)建部署java maven項目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Spring MVC利用Swagger2如何構(gòu)建動態(tài)RESTful API詳解
這篇文章主要給大家介紹了關(guān)于在Spring MVC中利用Swagger2如何構(gòu)建動態(tài)RESTful API的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10圖解Java經(jīng)典算法快速排序的原理與實現(xiàn)
快速排序是基于二分的思想,對冒泡排序的一種改進(jìn)。主要思想是確立一個基數(shù),將小于基數(shù)的數(shù)放到基數(shù)左邊,大于基數(shù)的數(shù)字放到基數(shù)的右邊,然后在對這兩部分進(jìn)一步排序,從而實現(xiàn)對數(shù)組的排序2022-09-09