Servlet過濾器是 Servlet 程序的一種特殊用法,主要用來完成一些通用的操做,如編碼的過濾、判斷用戶的登陸狀態。過濾器使得Servlet開發者可以在客戶端請求到達 Servlet資源以前被截獲,在處理以後再發送給被請求的Servlet資源,而且還能夠截獲響應,修改以後再發送給用戶。而Servlet監聽器能夠 監聽客戶端發出的請求、服務器端的操做,經過監聽器,能夠自動激發一些操做,如監聽在線人數。
Servlet過濾器簡介紹
Servlet過濾器是在Java Servlet 2.3 規範中定義的,它是一種能夠插入的Web組件,它可以對Servlet 容器的接收到的客戶端請求和向客戶端發出的響應對象進行截獲,過濾器支持對Servlet程序和JSP頁面的基本請求處理功能,如日誌、性能、安全、會話 處理、XSLT轉換等。
Servlet過濾器自己不產生請求和響應,它只提供過濾做用,Servlet過濾器可以在Servlet程序(JSP頁面)被調用以前檢查 request對象,修改請求頭和請求內容,在Servlet程序(JSP頁面)被調用以後,檢查response對象,修改響應頭和響應內容。html
Servlet過濾器的特色
1.Servlet過濾器能夠檢查和修改request和response對象。
2.Servlet過濾器能夠被指定與特定的URL關聯,只有當客戶請求訪問該特定的URL時,纔會觸發過濾器。
3.Servlet過濾器能夠被串聯成串,造成過濾鏈,協同修改請求和響應。
Servlet過濾器的做用以下:
1.查詢請求並做出相應的行動。
2.阻塞請求--響應對,使其不能進一步傳遞。
3.修改請求頭和內容,用戶能夠提供自定義的請求。
4.修改響應頭和內容,用戶能夠經過提供定製的響應版本實現。
5.與外部資源進行交互。
2 Servlet過濾器的體系結構
Servlet過濾器用於攔截傳入的請求和傳出的響應,並監視、修改或以某種方式處理 正在經過的數據流。Servlet過濾器是自包含、模塊化的組件,能夠將它們添加到請求/響應過濾鏈中,或者在不影響應用程序中其它Web組件的狀況下刪 除它們。Servlet過濾器只在改動請求和響應的運行時處理,於是不該該將它們之間嵌入到Web應用程序框架,除非是經過Servlet API中良好定義的標準接口來實現。
Web資源能夠配置成爲沒有過濾器與之關聯(默認狀況)、與單個過濾器關聯(典型狀況),甚至是與一個過濾器鏈關聯。其功能與Servlet一 樣,主要是接收請求和響應對象,而後過濾器會檢查請求對象,並決定是將該請求轉發給鏈中的下一個過濾器,仍是終止該請求並直接向客戶端發會一個響應,若是 請求被轉發了,它將被傳遞給過濾鏈中的下一個過濾器,或者Servlet程序(JSP頁面),在這個請求經過過濾器鏈並被服務器處理後,一個響應將以相反 的順序經過該過濾鏈發送回去,這樣就給每一個Servlet過濾器提供了根據須要處理響應對象的機會。
當過濾器在Servlet 2.3規範中首次引入時,只能過濾客戶端和客戶端所訪問的指定Web資源之間的內容(請求/響應),若是該Web資源將請求轉發給其它Web資源時,那就 不能向幕後委託的任何請求應用過濾器。Servlet 2.4 規範消除了這個限制,Servlet過濾器如今能夠應用於J2EE Web環境中存在請求和響應的任何地方。可見,Servlet過濾器能夠應用在客戶端和Servlet程序之間、Servlet程序和Servlet程序 之間、Servlet程序和JSP頁面之間、JSP頁面和JSP頁面之間,具備強大的能力和靈活性。
2.1 Servlet過濾器對請求的過濾
Servlet過濾器對請求的過濾過程以下:
1.Servlet容器建立一個Servlet過濾器實例。
2.Servlet過濾器實例調用init()方法獲得初始化參數。
3.Servlet過濾器實例調用doFilter()方法,根據初始化參數的值判斷該請求是否合法,若是該請求不合法,則阻塞該請求,若是是合法請求,則調用chain.doFilter(request,response)方法將該請求向後轉發。
13.2.2 Servlet過濾器對響應的過濾
Servlet過濾器對響應的過濾過程以下:
1.過濾器截獲客戶端的請求。
2.從新封裝ServletResponse,在封裝後的ServletResponse中提供客戶端自定義的輸出流。
3.將請求向後轉發。
4.Web組件產生響應。
5.過濾器從被封裝的ServletResponse中獲取客戶自定義的輸出流。
6.將響應內容經過客戶自定義的輸出流寫入緩衝流。
7.在緩衝流中修改響應內容後清空緩衝流,輸出響應內容。
2.3 Servlet過濾器的發佈
Seevlet過濾器設計完畢以後,必須對該過濾器進行發佈(配置), 發佈一個Servlet過濾器時,必須在項目的web.xml文件中加入<filter>元素和<filter- mapping>元素,<filter>元素用來定義一個過濾器,該元素的屬性有:java
屬性web |
描述sql |
filter-name數據庫 |
指定過濾器的名字編程 |
filter-class數組 |
指定過濾器類瀏覽器 |
init-param安全 |
指定過濾器的初始化參數服務器 |
<filter-mapping>元素用於將過濾器與URL關聯,其屬性有:
屬性 |
描述 |
filter-name |
指定過濾器的名字 |
url-pattern |
指定與過濾器關聯的URL |
3 實現一個Servlet過濾器
3.1 Servlet過濾器接口的構成
全部的Servlet過濾器都必須實現javax.servlet.filter接口,該接口中定義了3個過濾器必須實現的方法:
1.void init(FilterConfig):過濾器的初始化方法,Servlet容器在建立過濾器實例時調用這個方法,在這個方法中能夠讀出在web.xml文件中爲該過濾器配置的初始化參數。
2.voiddoFilter(ServletRequest,ServletResponse,FilterChain):用於完成實際的過濾操做,當客戶請求訪問與過濾器相關聯的URL時,Servlet容器將先調用過濾器的這個方法,FilterChain參數用於訪問後續過濾器。
3.voiddestroy():過濾器在被取消前執行這個方法,釋放過濾器申請的資源。
3.2 Servlet過濾器的建立步驟
建立一個Servlet過濾器須要下面的步驟:
1.建立一個實現了javax.servlet.Filter接口的類。
2.重寫init(FilterConfig)方法,讀入爲過濾器配置的初始化參數,申請過濾器須要的資源。
3.重寫方法doFilter(ServletRequest,ServletResponse,FilterChain),完成過濾操做,能夠 從ServletRequest參數中獲得所有的請求信息,從ServletResponse參數中獲得所有的響應信息。
4.在doFilter()方法的最後,使用FilterChain參數的doFilter()方法將請求和響應後傳。
5.對響應的Servlet程序和JSP頁面註冊過濾器,在部署描述文件(web.xml)中使用<filter-apping>和<filter>元素對過濾器進行配置。
3.3 編寫過濾器類
在過濾器中,須要使用3個簡單的接口,它們是:分別是Filter、FilterChain、FilterConfig,所有包含在javax.servlet包中。從編程的角度看,過濾器類要實現Filter接口,而後使用實現了FilterChain和FilterConfig接口的對象來工做,FilterChain對象負責將請求和響應後傳,FilterConfig對象負責爲過濾器讀初始化參數。
爲了與過濾器的三步模式(建立、工做、撤消)保持一致,過濾器必須重寫Filter接口中的三個方法:
init():在容器實例化過濾器市時被調用,主要爲過濾器作初始化,該方法有一個FilterConfig類型的形參。
doFilter():這個方法用來完成真正的過濾操做,它有3個形式參數:ServletRequest參數包含請求信息,ServletResponse參數包含響應信息,FilterChain參數用來將請求和響應向後傳遞。
destroy():過濾器被撤消時調用這個方法,釋放過濾器所 佔有的資源。
在下面的例子中實現了一個簡單的Servlet過濾器(SessionFilter.java),它實現的功能是判斷客戶是否成功登陸,若是成功登陸,轉向正確頁面,不然返回一個錯誤頁面,提示客戶應該進行登陸。該過濾器代碼以下:
//includeList:數組,受保護的資源。
//logonList:數組,登陸頁面。
package ch13;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class SessionFilter implements Filter{
String logonStrings,includeStrings,redirectPath,disabletestfilter;
String[] logonList,includeList;
private boolean isContains(String containers,String[] regx) {
boolean result=false;
for(int i=0;i<regx.length;i++) {
if (containers.indexOf(regx[i])!=-1)
return true;
}
return result;
}
public FilterConfig config;
private void setFilterConfig(FilterConfig config) {
this.config=config;
}
private FilterConfig getFilterConfig(){
return config;
}
//必須重寫
public void init(FilterConfig filterConfig) throws ServletException{
this.config=filterConfig;
logonStrings=config.getInitParameter("logonStrings");
includeStrings=config.getInitParameter("includeStrings");
redirectPath=config.getInitParameter("redirectPath");
disabletestfilter=config.getInitParameter("disabletestfilter");
logonList=logonStrings.split(";");//分割爲數組
includeList=includeStrings.split(";");//分割爲數組
}
//必須重寫
public void doFilter(ServletRequest request,ServletResponse response,FilterChain
chain) throws ServletException, IOException {
HttpServletRequest httpreq=(HttpServletRequest)request;
HttpServletResponse httpres=(HttpServletResponse)response;
HttpServletResponseWrapper wrapper=new HttpServletResponseWrapper(
(HttpServletResponse)response);
if (disabletestfilter.toUpperCase().equals("Y")){
chain.doFilter(request,response);//若是不過濾
return;
}
Object user=httpreq.getSession().getAttribute("userinfo");
if (user==null){//該用戶沒有登陸
if (!isContains(httpreq.getRequestURI(),includeList)){
chain.doFilter(request,response);
return;//訪問的是不受保護的頁面,能夠
}
if (isContains(httpreq.getRequestURI(),logonList)){
chain.doFilter(request,response);
return; //訪問的是登陸頁面,能夠
}
wrapper.sendRedirect(redirectPath); //轉向登頁面
}else {//該用戶已經登陸
chain.doFilter(request,response);
}
}
//必須重寫
public void destroy() {
config=null;
}
}
在上面的這個Servlet過濾器程序中,根據用戶session對象中有無userinfo這個屬性來肯定該用戶是否已經登陸。
3.4 配置部署過濾器
在WEB-INF/web.xml文件中用如下代碼配置過濾器:
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>ch13.SessionFilter</filter-class>
<init-param>
<param-name>logonStrings</param-name>
<param-value>Login.jsp</param-value>
</init-param>
<init-param>
<param-name>includeStrings</param-name>
<param-value>.jsp;.html;.htm</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name>
<param-value>./Login.jsp</param-value>
</init-param>
<init-param>
<param-name>disabletestfilter</param-name>
<param-value>n</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在上面的配置中,參數logonStrings指定能夠訪問的登陸頁面,參數includeStrings指定受保護的資源的後綴,參數redirectPath表示沒有登陸時轉向的登陸頁面,參數disabletestfilter表示過濾器是否有效。而 /* 表示過濾器與全部的URL都關聯(對全部的訪問請求都進行過濾)。在瀏覽器中訪問任意的資源時,都要經過這個過濾器的過濾。
4 過濾器的應用案例.4.1 版權過濾器的應用案例
在一個Web應用中的全部頁面的下面添加上版權信息,一般的作法是採用<%@ include>指令或<c:import> 標籤,使用過濾器也是一個好辦法。
1.編寫過濾器類CopyrightFilter.java
package ch13;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class CopyrightFilter implements Filter{
private String date;
public FilterConfig config;
//必須重寫
public void init(FilterConfig filterConfig) throws ServletException{
this.config=filterConfig;
date=config.getInitParameter("date");
}
//必須重寫
public void doFilter(ServletRequest request,ServletResponse response,FilterChain
chain) throws ServletException, IOException {
chain.doFilter(request,response);
PrintWriter out=response.getWriter();
out.print("<br><center><font size='3' color='red'>版權全部:北京工業大學
</center></font>");
if (date!=null)
out.print("<br><center><font color='blue'>"+date+"</center></font>");
out.flush();
}
//必須重寫
public void destroy() {
config=null;
}
}
在這個過濾器中,在doFilter()方法的最後,經過response對象獲得一個輸出流out,而後經過輸出流向客戶端輸出版權信息,這樣,每一個頁面的最後都會出現過濾器添加的版權信息。
2.修改web.xml,配置該過濾器
<filter>
<filter-name>CopyrightFilter</filter-name>
<filter-class>ch13.CopyrightFilter</filter-class>
<init-param>
<param-name>date</param-name>
<param-value>2010-9</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CopyrightFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.測試
在瀏覽器中任意訪問一個頁面,均可以在看到在頁面的下部出現過濾器添加的版權信息。
4.2 禁止未受權的IP訪問站點過濾器的應用案例
使用過濾器禁止未受權的IP訪問站點是過濾器常見的應用,本例演示瞭如何利用過濾器實現禁止未受權的IP訪問站點。
1.編寫過濾器類FilterIP.java
package ch13;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class FilterIP implements Filter{
private String filterIP,error;
public FilterConfig config;
//必須重寫
public void init(FilterConfig filterConfig) throws ServletException{
this.config=filterConfig;
filterIP=config.getInitParameter("FilterIP");
if (filterIP==null) filterIP="";
error=config.getInitParameter("ERROR");
if (error==null) error="error.jsp";
}
//必須重寫
public void doFilter(ServletRequest request,ServletResponse response,FilterChain
chain) throws ServletException, IOException {
RequestDispatcher dispatcher=request.getRequestDispatcher("ErrorInfo.jsp");
String remoteIP=request.getRemoteAddr();//獲得客戶的IP地址
if (remoteIP.equals(filterIP)) {
dispatcher.forward(request,response);
return;
} else
chain.doFilter(request,response);
}
//必須重寫
public void destroy() {
config=null;
}
}
在這個過濾器中,在doFilter()方法內,經過request對象獲得客戶端的IP地址,若是客戶端的IP是被禁止的IP,則使用request對象將請求轉發給一個出錯頁面。
2.修改web.xml,配置過濾器
<filter>
<filter-name>FilterIP</filter-name>
<filter-class>ch13.FilterIP</filter-class>
<init-param>
<param-name>FilterIP</param-name>
<param-value>192.168.1.1</param-value>
</init-param>
<init-param>
<param-name>ERROR</param-name>
<param-value>error.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterIP</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
對來自192.168.1.1的客戶的全部請求(/*)都進行過濾,轉移到error.jsp頁面。
3.編寫出錯頁面error.jsp
<%@ page contentType="text/html;charset=gb2312" %>
網站不容許IP地址爲192.168.1.1的計算機訪問。
在IP地址爲 192.168.1.1 的計算機上訪問網站的任何一個資源,都會轉移到error.jsp頁面。
4.3 過濾頁面內容(響應內容)
本過濾器使用HttpServletResponseWrapper類 來實現頁面內容的過濾,它的原理是讓Web資源先將頁面內容(響應內容)寫入到HttpServletResponseWrapper對象中,而後再在過 濾器中處理HttpServletResponseWrapper對象中的頁面內容(響應內容),最後再將處理好的頁面內容(響應內容)發送給客戶。
1.編寫HttpServletResponseWrapper類的子類.java
package ch13;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.ServletOutputStream;
public class WrapperResponse extends HttpServletResponseWrapper {
public static final int OT_NONE = 0, OT_WRITER = 1, OT_STREAM = 2;
private int outputType = OT_NONE;
private ServletOutputStream output = null;
private PrintWriter writer = null;
private ByteArrayOutputStream buffer = null;
//構造函數
public WrapperResponse(HttpServletResponse resp) throws IOException {
super(resp);
buffer = new ByteArrayOutputStream();
}
//獲得字符輸出流
public PrintWriter getWriter() throws IOException {
if (outputType == OT_STREAM)
throw new IllegalStateException(); //已經用了OutputStream流
else if (outputType == OT_WRITER)
return writer;
else {
outputType = OT_WRITER;
writer = new PrintWriter(new OutputStreamWriter(buffer, getCharacterEncoding()));
return writer;
}
}
//獲得字節輸出流
public ServletOutputStream getOutputStream() throws IOException {
if (outputType == OT_WRITER)
throw new IllegalStateException(); //已經用了Writer流
else if (outputType == OT_STREAM)
return output;
else {
outputType = OT_STREAM;
output = new WrappedOutputStream(buffer);
return output;
}
}
//刷新輸出內容
public void flushBuffer() throws IOException {
if (outputType == OT_WRITER)
writer.flush();
if (outputType == OT_STREAM)
output.flush();
}
//輸出緩衝區復位
public void reset() {
outputType = OT_NONE;
buffer.reset();
}
public String getResponseData() throws IOException {
flushBuffer();
return new String(buffer.toByteArray());
}
//內部類,將數據寫入本身的定義的緩衝區
class WrappedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream buffer;
public WrappedOutputStream(ByteArrayOutputStream buffer) {
this.buffer = buffer;
}
public void write(int b) throws IOException {
buffer.write(b);
}
public byte[] toByteArray() {
return buffer.toByteArray();
}
}
}
在這個類中,必定要重寫response對象的關於輸出流(outputStream、writer)操做的方法:getOutputStream()、getWriter()、flushBuffer()、reset()。
2.編寫過濾器GavinFilter.java
package ch13;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GavinFilter implements Filter {
private String oldword="%" , newword="百分號";
public void destroy(){}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain
chain) throws IOException, ServletException {
HttpServletResponse oldresponse = (HttpServletResponse)response;
WrapperResponse wrapperResponse = new WrapperResponse(oldresponse);
chain.doFilter(request, wrapperResponse); //讓服務器將響應內容寫到Wrapper中
String html = wrapperResponse.getResponseData(); //取出響應內容
oldresponse.getWriter().print(html.replaceAll(oldword, newword)); //替換頁面中的文字,而後發送給客戶
}
public void init(FilterConfig config) throws ServletException {
oldword=config.getInitParameter("oldword");
newword=config.getInitParameter("newword");
}
}
該過濾器將頁面內容(響應內容)中的字符 % 替換爲百分號三個漢字,因而可知,實現了對響應內容的過濾。
3.對該過濾器的配置
<filter>
<filter-name>gavinFilter</filter-name>
<filter-class>ch13.GavinFilter</filter-class>
<init-param>
<param-name>oldword</param-name>
<param-value>%</param-value>
</init-param>
<init-param>
<param-name>newword</param-name>
<param-value>百分號</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>gavinFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5 Servlet監聽器
Servlet監聽器也叫作 listener,經過它能夠監聽Web應用的上下文(環境)信息、Servlet請求信息、Servlet會話信息,並自動根據不一樣狀況,在後臺調用相 應的處理程序。經過監聽器,能夠自動激發一些操做,好比監聽在線人數,當增長一個HttpSession時就激發 sessionCreated(HttpSessionEvent)方法,這樣就能夠給在線人數加1。
13.5.1 監聽器的原理
Servlet監聽器是Web應用開發的一個重要組成部 分,Servlet監聽器是在Servlet2.3規範中和Servlet過濾器一塊兒引入的。在 Servlet2.4 規範中對其進行了比較大的改進。主要就是用來對Web應用進行監督和控制,極大地加強了Web應用的事件處理能力。
Servlet監聽器的功能比較相似於Java中的GUI程序的監聽器,能夠監聽因爲Web應用中的狀態改變而引發的Servlet容器產生的相應事件,而後接收並處理這些事件。
5.2 監聽器的類型
在 Servlet 2.4 規範中,根據監聽對象的類型和範圍,將監聽器分爲3類:ServletRequest監聽器(請求監聽器)、HttpSession監聽器(會話監聽器)、ServletContext監聽器(上下文監聽器),其中請求監聽器(ServletRequest監聽器)是 Servlet 2.4 規範中新增長的監聽器,能夠用來監聽客戶的端請求,在Servlet 2.4 規範中包含了8個監聽器接口和6個監聽器事件類,具體的監聽器接口和事件以下表:
監聽對象 |
監聽接口 |
監聽事件 |
ServletRequest |
ServletRequestListener (2個方法) |
ServletRequestEvent |
ServletRequestAttributeListener (3個方法) |
ServletRequestAttributeEvent |
|
HttpSession |
HttpSessionListener (2個方法) |
HttpSessionEvent |
HttpSessionActivationListener (2個方法) |
||
HttpSessionAttributeListener (3個方法) |
HttpSessionBindingEvent |
|
HttpSessionBindingListener (2個方法) |
||
ServletContext |
ServletContextListener (2個方法) |
ServletContextEvent |
ServletContextAttributeListener (3個方法) |
ServletContextAttributeEvent |
1.被監聽對象ServletContext
對ServletContext對象(JSP頁面中稱爲application對象)實現監聽涉及2個接口:
(1)ServletContextListener接口:用於監聽ServletContext對象的建立和刪除:接口中定義的回調方法有:
當建立一個ServletContext對象時,激發 contextInitialzed(ServletContextEvent)方法。
當撤消一個ServletContext對象時,激發 contextDestroyed(ServletContextEvent)方法。
(2)ServletContextAttributeListener接口:用於監聽ServletContext對象的屬性操做。接口中定義的回調方法有:
增長屬性時,激發 attributeAdded(ServletContextAttributeEvent)
刪除屬性時,激發 attributeRemoved(ServletContextAttributeEvent)
修改屬性時,激發 attributeReplaced(ServletContextAttributeEvent)
2.被監聽對象HttpSession
對HttpSession對象(session)實現監聽涉及4個接口:
(1)HttpSessionListener接口:這個接口監聽Http會話的建立和撤消,並在某個session對象創建和銷燬以前調用某個方法。接口中定義的回調方法有:
建立一個session對象時,激發 sessionCreated(HttpSessionEvent)
刪除一個session對象時,激發 sessionDestroyed(HttpSessionEvent)
(2)HttpSessionActivationListener接口:監聽Http會話的active和passivate狀態。接口中定義的回調方法有:
session對象被保存到磁盤時,激發 sessionWillPassivate(HttpSessionEvent)
session對象被調入內存時,激發 sessionDidActivate(HttpSessionEvent)
Activate與Passivate是用於置換session對象的動做,當Web服務器由於資源利用或負載平衡等緣由要將內存中的 session對象暫時儲存至硬盤或其它儲存器時(經過對象序列化),所做的動做稱之爲Passivate,而硬盤或儲存器上的session對象從新加 載到JVM中時所採的動做稱之爲Activate。sessionDidActivate()方法與 sessionWillPassivate()方法分別於Activeate後與Passivate前被調用。
(3)HttpSessionAttributeListener接口:監聽Http會話中屬性的設置信息。接口中定義的回調方法有:
向某個session對象中增長新屬性時,激發 attributeAdded(HttpSessionBindingEvent)
刪除某個session對象中的屬性時,激發 attributeRemoved(HttpSessionBindingEvent)
修改某個session對象中的屬性時,激發 attributeReplaced(HttpSessionBindingEvent)
使用HttpSessionBindingEvent事件類對象的getSession()方法能夠獲得這個session對象,使用 HttpSessionBindingEvent對象的getName()方法獲得屬性的名字,使用getValue()方法獲得屬性的值。
如有屬性加入到某個會話(HttpSession)對象,則會調用attributeAdded(),同理在替換屬性與移除屬性時,會分別調用attributeReplaced()、attributeRemoved()。
(4)HttpSessionBindingListener接口:這是惟一一個不須要在web.xml中進行配置的監聽器接口,監聽Http會話中屬性的變化狀況。接口中定義的回調方法有:
屬性被加入到session中時,激發屬性的 valueBound(HttpSessionBindingEvent)
屬性被從session中刪除時,激發屬性的 valueUnbound(HttpSessionBindingEvent)
使用HttpSessionBindingEvent事件類對象的getSession()方法能夠獲得這個session對象,使用 HttpSessionBindingEvent對象的getName()方法獲得屬性的名字,使用getValue()方法獲得屬性的值。
若是一個對象object實現了HttpSessionBindingListener接口時,當把object對象保存到session中時, 就會自動調用object對象的valueBound()方法,若是對象object被從session(HttpSession)移除時,則會調用 object對象的valueUnbound()方法。使用這個接口,可讓一個對象本身知道它本身是被保存到了session中,仍是從session 中被刪除了。
3.被監聽對象ServletRequest
對ServletRequest對象(request)實現監聽涉及2個接口:
(1)ServletRequestListener接口:監聽請求的建立和撤消,該接口用來監聽請求到達和結束,所以能夠在請求達到前和請求結束前執行一些用戶行爲。 接口中定義的回調方法有:
請求對象初始化時,激發 requestInitialized(ServletRequestEvent)
請求對象被撤消時,激發 requestDestroyed(ServletRequestEvent)
在request(HttpServletRequest)對象創建或被消滅時,會分別調用requestInitialized()和requestDestroyed()方法。
(2)ServletRequestAttributeListener接口:監聽請求中(request對象中)的屬性變化。接口中定義的回調方法有:
向某個request對象中增長屬性時被調用attributeAdded(ServletRequestAttributeEvent)方法。
從某個request對象中刪除屬性時被調用attributeRemoved(ServletRequestAttributeEvent)方法。
修改某個request中的屬性時被調用attributeReplaced(ServletRequestAttributeEvent)方法。
使用ServletRequestEvent類的getServletRequest()方法能夠獲得這個被監聽的請求對象,使用 ServletRequestAttributeEvent類的getName()方法能夠獲得屬性名,getValue()方法能夠獲得屬性的值。
如有屬性加入到某個request對象中時則會調用attributeAdded(),同理在替換屬性與刪除屬性時,會分別調用attributeReplaced()、 attributeRemoved()。
當Web應用程序啓動後,在處理任何請求以前,調用contextInitialzed()方法和getInitParamter()方法,返回 在配置文件中爲定義的環境初始化信息。不一樣的組件,如Servlet、JSP、監聽器和過濾器等,經過ServletRequest、 HttpSession 和 ServletContext達到數據共享,這些類都提供了下面的一組方法,可使用這組方法來設置、獲取、刪除屬性:
public void setAttribute("屬性名",屬性值);
public Object getAttribute("屬性名");
public void removeAttribute("屬性名");
5.3 監聽器管理共享數據庫鏈接
在web.xml中,使用<listener>來配置監聽器,語法是:
<listener>
<listener-class>包名.類名</listener-class>
</listener>
好比:建立一個ServletContext對象監聽器,在一個Web項目一啓動就建立一個與數據庫的鏈接,保存在application對象中,這個鏈接一直保存到Web項目關閉時爲止。程序代碼以下:
package ch13;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.*;
//import ch7.db.*;
public final class MyConnectionManager implements ServletContextListener {
Connection con=null;
public void contextInitialized(ServletContextEvent e) {//重寫接口定義的方法,項目啓動是調用該方法
ConnectDB db=new ConnectDB();
con=db.getConnection(); //使用對象db建立數據庫鏈接
e.getServletContext().setAttribute("con",con);//與數據庫的鏈接保存入application對象中
}
public void contextDestroyed(ServletContextEvent e) {//重寫接口定義的方法,項目關閉時調用該方法
try {
con.close();
}
catch(Exception e1){}
}
}
在web.xml文件對這個ServletContext類型的監聽器進行配置:
<listener>
<listener-class>ch13.MyConnectionManager</listener-class>
</listener>
這個監聽器能保證每新建立一個ServletContext對象時(一個Web項目只有一個 ServletContext對象),該Web項目都會有一個可使用的數據庫鏈接,而且這個數據庫鏈接會在該ServletContext對象關閉(結 束)的時候隨之關閉。
測試頁面testcon.jsp:
<%@ page contentType="text/html" pageEncoding="GB18030"%>
<br><%= "獲得的數據庫鏈接:"+application.getAttribute("con") %>
<br><h1>請注意安裝數據庫的驅動程序</h1>
5.4 監聽器的應用案例
下面是一個在線用戶數量監聽器,這個監聽器能夠實時統計在線人數,在 ServletContext初始化和撤消時,在服務器控制檯打印出對應信息,當ServletContext對象裏的屬性增長、修改、刪除時,在服務器 控制檯打印相應的信息。要完成上面的監聽功能,須要使用3個接口:
HttpSessionListener:監督HttpSession對象的建立和撤消,統計人數。
ServletContextListener:監督ServletContext對象的建立和撤消。
ServletContextAttributeListener:監督ServletContext的屬性變化。
1.監聽器程序代碼OnLineCountListener.java
package ch13;
import javax.servlet.*;
import javax.servlet.http.*;
public final class OnLineCountListener implements HttpSessionListener,
ServletContextAttributeListener, ServletContextListener {
private int count;
private ServletContext context=null;
//構造函數
public OnLineCountListener() {
count=0;//人數
}
//重寫HttpSessionListener接口中的2個方法,完成對session對象建立和撤消的監視
public void sessionCreated(HttpSessionEvent se) {//建立了一個session對象
count++;//人數加1
setContext(se);
}
public void sessionDestroyed(HttpSessionEvent se){//撤消了一個session對象
count--;//人數減1
setContext(se);
}
private void setContext(HttpSessionEvent se){
se.getSession().getServletContext().setAttribute("onLine",new Integer(count));
}
//重寫ServletContextAttributeListener接口中的3個方法
public void attributeAdded(ServletContextAttributeEvent event) {//添加了屬性
log("attributeAdded("+event.getName()+","+event.getValue()+")");
}
public void attributeRemoved(ServletContextAttributeEvent event) {//刪除了屬性
log("attributeRemove("+event.getName()+","+event.getValue()+")");
}
public void attributeReplaced(ServletContextAttributeEvent event) {//替換了原有的屬性
log("attributeReplaced("+event.getName()+","+event.getValue()+")");
}
//重寫ServletContextListener接口中的2個方法
public void contextDestroyed(ServletContextEvent event) {//Web項目關閉
log("contextDestroyed()");
context=null;
}
public void contextInitialized(ServletContextEvent event) {//Web項目啓動
this.context=event.getServletContext();
log("contextInitialized()");
}
//顯示信息
private void log(String message){
System.out.println("ContextListener:"+message);
}
}
在OnLineCountListener類中,用count保存目前在線人數,每增長一個session對象,人數加1,每撤消一個session對象,人數減1。人數保存在ServletContext對象中,使得任何頁面均可以使用。
2.在web.xml文件中配置監聽器
<listener>
<listener-class>ch13.OnLineCountListener</listener-class>
</listener>
3.編寫測試頁面(2個)
listener.jsp------>exit.jsp
listener.jsp頁面內容
<%@ page contentType="text/html;charset=gb2312" %>
目前在線人數:<font color="red"><%=application.getAttribute("onLine")%></font><br>
退出會話:
<form action="exit.jsp" method="post">
<input type="submit" value="exit">
</form>
exit.jsp頁面內容
<%@ page contentType="text/html;charset=gb2312" %>
你已經退出會話<% session.invalidate(); %>
能夠單獨啓動5個瀏覽器窗口,每一個窗口表明一個客戶,所以在線人數是5。
5.5 HttpSessionBindingListener 接口的使用
設計一個學生對象Student,當將該學生對象存入 session中時,他的年齡增長10歲,當將這個學生對象從session中刪除時,他的年齡減小5歲。
學生類Student.java
package ch13;
import javax.servlet.*;
import javax.servlet.http.*;
public class Student implements HttpSessionBindingListener { private int age=30; public void valueBound(HttpSessionBindingEvent arg0) {//存入session時自動調用 age+=10; } public void valueUnbound(HttpSessionBindingEvent arg0) {//從session中刪除時自動調用 age-=5; } public int getAge() {return age;} } 測試頁面bind.jsp <%@ page contentType="text/html;charset=gb2312" import="ch13.Student"%> <% Student student=new Student(); out.println("學生年齡:"+student.getAge()+"<br>"); session.setAttribute("st",student); out.println("存入session後,該學生年齡:"+student.getAge()+"<br>"); session.removeAttribute("st"); out.println("從session刪除,該學生年齡:"+student.getAge()+"<br>"); %>