Servlet Filtering 過濾器(filter)是Java類,可以改變請求(request)和響應(response)的頭信息與內(nèi)容信息,。過濾器不同于其他Web組件的地方是它本身并不創(chuàng)建響應(response),,然而它可以依附在任何類型的Web資源上。過濾器截取請求(request),,檢查和改變request對象,、response對象,并可以執(zhí)行一些其他的任務,。過濾器提供的主要功能是:
過濾器截獲對特定命名的一個資源和一組資源的請求(request),,然后執(zhí)行過濾器中的代碼。對于特定的資源,,可以指定按照一定順序調(diào)用的一個和多個過濾器,,這就組成了鏈(chain)。使用過濾器主要包括:
編寫過濾器的API是javax.servlet包中Filter,、FilterChain和FilterConfig接口中定義的一些方法,。定義一個過濾器就是實現(xiàn)Filter接口。Filter接口中最主要的方法是doFilter()方法,,它接收三個參數(shù):request對象,、response對象、filterchain對象,。 void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)這個方法能夠執(zhí)行的動作包括:
除了doFilter()方法,,開發(fā)人員也必須實現(xiàn)init()和destroy()方法。當容器創(chuàng)建過濾器實例時調(diào)用init()方法,, void init(FilterConfig filterConfig)可以從FilterConfig對象中獲得初始化參數(shù),。 在doFilter()方法中,過濾器可以從FilterConfig對象獲得ServletContext對象,,那么就可以訪問存儲在ServletContext中的屬性對象,。當過濾器完成特定的處理過程后,調(diào)用chain對象的doFilter()方法,。例如 public final class HitCounterFilter implements Filter { private FilterConfig filterConfig = null; // 初始化 public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } // 結束 public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) return; StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); writer.println(' ... '); . . . writer.flush(); // 輸出日志 filterConfig.getServletContext(). log(sw.getBuffer().toString()); ... //調(diào)用在過濾器鏈中的下一個實體 chain.doFilter(request, wrapper); ... }}6.5.1.2. 定制請求和響應有許多方法可以改變請求(request)或者響應(response),。例如過濾器能夠給請求(request)增加屬性或在響應(response)中插入數(shù)據(jù)。 過濾器如果需要改變響應(response)必須在響應(response)返回給客戶端之前捕獲它,。要做到這一點需要傳遞“替身”流(stream)給Servlet,,然后利用“替身”stream產(chǎn)生響應(response)?!疤嫔怼眘tream防止了當響應(response)結束后關閉了原始的響應(response)輸出流,,并且允許過濾器改變Servlet的響應(response)。 過濾器產(chǎn)生“替身”stream
為了給Servlet傳遞“替身”stream,,過濾器需要創(chuàng)建response對象的包裝類并且覆蓋getWriter或getOutputStream方法,。包裝類被FilterChain對象的doFilter方法傳遞。創(chuàng)建請求(request)的包裝類繼承ServletRequestWrapper或HttpServletRequestWrapper,,創(chuàng)建響應(response)的包裝類繼承ServletResponseWrapper或HttpServletResponseWrapper,。 以下CharResponseWrapper類包裝了響應(response): public class CharResponseWrapper extends HttpServletResponseWrapper { private CharArrayWriter output; public String toString() { return output.toString(); } public CharResponseWrapper(HttpServletResponse response){ super(response); output = new CharArrayWriter(); } public PrintWriter getWriter(){ return new PrintWriter(output); }}包裝類被傳遞給BookStoreServlet,BookStoreServlet把響應寫入“替身”stream,,當chain.doFilter返回,,HitCounterFilter重新找回response把它寫入緩沖,過濾器插入計數(shù)器值到緩沖中然后重新設置response的頭信息,,最后把緩沖中的內(nèi)容寫入response,。 PrintWriter out = response.getWriter();//構造包裝類CharResponseWrapper wrapper = new CharResponseWrapper( (HttpServletResponse)response);//向 doFilter 傳遞包裝類chain.doFilter(request, wrapper);CharArrayWriter caw = new CharArrayWriter();caw.write(wrapper.toString().substring(0, wrapper.toString().indexOf('</body>')-1));...caw.write('\n</body></html>');//重新設置響應長度response.setContentLength(caw.toString().length());out.write(caw.toString());out.close();6.5.1.3. 映射過濾器Web容器使用過濾器映射來決定是否過濾Web資源,。在Web應用的部署描述文件中映射過濾器到Servlet或URL模板。
如下圖,,可以映射一個過濾器到一個或多個Servlet,或者可以映射一個 Servlet到多個過濾器,。 過濾器的映射
過濾器F1映射到servlet S1,,S2和S3,過濾器F2映射到servlet S2,,過濾器F3 映射到servlet S1和S2,。 6.5.2. Application Events應用事件模型提供了當ServletContext,HttpSession,,ServletRequest狀態(tài)改變時的通知功能,。可以編寫事件監(jiān)聽類來響應這些狀態(tài)的改變,,并且可以配置和部署應用事件和監(jiān)聽類到Web應用,。 對于ServletContext事件,當Web應用部署,、卸載和對context增加屬性時,,事件監(jiān)聽類可以得到通知,。下表列出了ServletContext的事件類型,對應特定事件的監(jiān)聽類必須實現(xiàn)的接口和當事件發(fā)生時調(diào)用的方法,。 事件類型 接口 方法 Servlet context被創(chuàng)建 javax.servlet.ServletContextListener contextInitialized() Servlet context被注銷 javax.servlet.ServletContextListener contextDestroyed() 增加屬性 javax.servlet. ServletContextAttributesListener attributeAdded() 刪除屬性 javax.servlet. ServletContextAttributesListener attributeRemoved() 屬性被替換 javax.servlet. ServletContextAttributesListener attributeReplaced()對于HttpSession事件,,當session激活、刪除或者session屬性的增加,、刪除和替換時,,事件監(jiān)聽類得到通知。下表列出了HttpSession的事件類型,,對應特定事件的監(jiān)聽類必須實現(xiàn)的接口和當事件發(fā)生時調(diào)用的方法,。 事件類型 接口 方法 session激活 javax.servlet.http. HttpSessionListener sessionCreated() session刪除 javax.servlet.http. HttpSessionListener sessionDestroyed() 增加屬性 javax.servlet.http. HttpSessionAttributesListener attributeAdded() 刪除屬性 javax.servlet.http. HttpSessionAttributesListener attributeRemoved() 屬性被替換 javax.servlet.http. HttpSessionAttributesListener attributeReplaced()對于ServletRequest事件,當request初始化,、銷毀或者request屬性的增加,、刪除和替換時,事件監(jiān)聽類得到通知,。下表列出了ServletRequest的事件類型,,對應特定事件的監(jiān)聽類必須實現(xiàn)的接口和當事件發(fā)生時調(diào)用的方法。 事件類型 接口 方法 session初始化 javax.servlet.ServletRequestListener requestInitialized() session銷毀 javax.servlet.ServletRequestListener requestDestroyed() 增加屬性 javax.servlet.ServletRequestAttributeListener attributeAdded() 刪除屬性 javax.servlet.ServletRequestAttributeListener attributeRemoved() 屬性被替換 javax.servlet.ServletRequestAttributeListener attributeReplaced() 6.5.2.1. 配置事件監(jiān)聽類配置事件監(jiān)聽類的步驟:
編寫事件監(jiān)聽類的步驟:
ServletContext 監(jiān)聽類例子: import javax.servlet.*;public final class myContextListenerClass implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { /* 當 ServletContext 初始化時被調(diào)用,,可以在這兒 初始化 ServletContext 的相關數(shù)據(jù) */ } public void contextDestroyed(ServletContextEvent event) { /* 當 Web 應用被卸載或 Apusic 服務器關閉時被調(diào)用 */ }}HttpSession 屬性監(jiān)聽類例子: import javax.servlet.*;public final class mySessionAttributeListenerClass implements HttpSessionAttributesListener { public void attributeAdded(HttpSessionBindingEvent sbe) { /* 增加session屬性時被調(diào)用 */ } public void attributeRemoved(HttpSessionBindingEvent sbe) { /* 刪除session屬性時被調(diào)用 */ } public void attributeReplaced(HttpSessionBindingEvent sbe) { /* 替換session屬性時被調(diào)用 */ }}ServletRequest 屬性監(jiān)聽類例子: import javax.servlet.*;public final class myRequestAttributeListenerClass implements ServletRequestAttributeListener { public void attributeAdded(HttpSessionBindingEvent sbe) { /* 增加request屬性時被調(diào)用 */ } public void attributeRemoved(HttpSessionBindingEvent sbe) { /* 刪除request屬性時被調(diào)用 */ } public void attributeReplaced(HttpSessionBindingEvent sbe) { /* 替換request屬性時被調(diào)用 */ }}----------------------------------------------- ----------------------------------------------- Servlet和Filter的url匹配以及url-pattern詳解Servlet和filter是J2EE開發(fā)中常用的技術,,使用方便,配置簡單,,老少皆宜,。估計大多數(shù)朋友都是直接配置用,也沒有關心過具體的細節(jié),,今天遇到一個問題,,上網(wǎng)查了servlet的規(guī)范才發(fā)現(xiàn),servlet和filter中的url-pattern還是有一些文章在里面的,,總結了一些東西,,放出來供大家參考,以免遇到問題又要浪費時間,。 一,,servlet容器對url的匹配過程:
當一個請求發(fā)送到servlet容器的時候,,容器先會將請求的url減去當前應用上下文的路徑作為servlet的映射url,比如我訪問的是http://localhost/test/aaa.html,,我的應用上下文是test,,容器會將http://localhost/test去掉,剩下的/aaa.html部分拿來做servlet的映射匹配,。這個映射匹配過程是有順序的,,而且當有一個servlet匹配成功以后,就不會去理會剩下的servlet了(filter不同,,后文會提到),。其匹配規(guī)則和順序如下: 1. 精確路徑匹配。例子:比如servletA 的url-pattern為 /test,,servletB的url-pattern為 /* ,,這個時候,如果我訪問的url為http://localhost/test ,,這個時候容器就會先 進行精確路徑匹配,,發(fā)現(xiàn)/test正好被servletA精確匹配,那么就去調(diào)用servletA,,也不會去理會其他的servlet了,。 2. 最長路徑匹配。例子:servletA的url-pattern為/test/*,,而servletB的url-pattern為/test/a/*,此時訪問http://localhost/test/a時,,容器會選擇路徑最長的servlet來匹配,,也就是這里的servletB。 3. 擴展匹配,,如果url最后一段包含擴展,,容器將會根據(jù)擴展選擇合適的servlet。例子:servletA的url-pattern:*.action 4. 如果前面三條規(guī)則都沒有找到一個servlet,,容器會根據(jù)url選擇對應的請求資源,。如果應用定義了一個default servlet,則容器會將請求丟給default servlet(什么是default servlet,?后面會講),。
根據(jù)這個規(guī)則表,就能很清楚的知道servlet的匹配過程,,所以定義servlet的時候也要考慮url-pattern的寫法,,以免出錯。 對于filter,,不會像servlet那樣只匹配一個servlet,,因為filter的集合是一個鏈,,所以只會有處理的順序不同,而不會出現(xiàn)只選擇一個filter,。Filter的處理順序和filter-mapping在web.xml中定義的順序相同,。 二,url-pattern詳解在web.xml文件中,,以下語法用于定義映射: l 以”/’開頭和以”/*”結尾的是用來做路徑映射的,。 l 以前綴”*.”開頭的是用來做擴展映射的。 l “/” 是用來定義default servlet映射的,。 l 剩下的都是用來定義詳細映射的,。比如: /aa/bb/cc.action 所以,為什么定義”/*.action”這樣一個看起來很正常的匹配會錯,?因為這個匹配即屬于路徑映射,,也屬于擴展映射,導致容器無法判斷,。 |
|
來自: JourneyHappy > 《待分類》