文章目錄
-------------------------------------------------
一.指令元素
1.page指令
import
session
contentType
buffer
isTreadSafe
info
errorPage
isErrorPage
2.include指令
3.taglib指令
二.腳本元素
1.聲明元素
2.表達式元素
3.腳本元素
4.注釋元素
三.標準動作元素
1.<jsp:param>
2.<jsp:include>
3.<jsp:forward>
4.<jsp:plugin>
5.<jsp:useBean>
6.<jsp:setProperty>
7.<jsp:getProperty>
四.內置對象
1.request
2.response
3.out
4.session
5.pageContext
6.application
7.config
8.page
9.exception
五.JavaBeans的使用
1.JavaBeans在JSP中的基本使用格式
2.scope范圍的具體設定
3.session事件的運用
4.Bean的保存與讀取
六.JSP中的文件操作
七.JSP運行原理剖析
-------------------------------------------------
在早期,開發網絡數據庫應用程序主要采用CGI(Common Gateway Interface)技術。編寫CGI程序可以使用不同的程序語言,如Perl、Visual Basic、Delphi或C/C++等。雖然CGI技術已經發展成熟而且功能強大,但由于其編程困難、效率低下、修改復雜等缺陷,所以有被新技術取代的技術。
在這樣的背景下,新的技術紛紛面世,如ASP(Active Server Page)、PHP(Personal Home Page)、JSP(Java Server Page)等。其中,JSP被許多人認為是未來最有發展前途的動態網站技術。
JSP頁面一般由HTML標簽和JSP元素構成,其中的JSP元素則又是由“指令元素”、“腳本元素” 、“標準動作元素” 、“內置對象”四個部分組成。下面,就讓我們一起來探究JSP的奧秘吧……
一. 指令元素
可以把JSP理解為用來通知JSP引擎的消息。JSP不直接生成可見的輸出,用JSP指令設置JSP引擎處理JSP頁面的機制。
一般JSP指令用標簽<%@…%>表示,JSP指令包括page、include和taglib。page指令是針對當前頁面的指令,而include指令用來指定如何包含另外一個文件,taglib指令用來定義和訪問自定義標記庫。這三種指令通常都有默認值,這樣開發人員就不必顯式的使用每一個指令予以確認。
1. page指令
page指令的設置語法格式是:<%@ page attribute1=”value1” attribute2=”value2”…%>
下面介紹指令中包括的幾個常用屬性,并作簡要說明。
l import
import指令是所有page指令中,唯一可以多次設置的指令,而且累加每個設置。它用來指定jsp網頁中所需要使用到的一些類。例如:
<%@ page import=”java.io.*,java.util.Date”%>
l session
定義當前頁面是否參與http會話。當設置為”true”時,可以獲得隱含名為session的對象,為”false”時,則不能。默認設置為”true”。
l contentType
設置jsp網頁輸出時數據時,所使用的字符壓縮方式,以及所使用的字符集,當編寫中文網頁時,設置如下:
<%@page contentType=”text/html;charset=Gb2312”%>
此屬性的默認值為”text/html;charset=ISO-8859-1”。
l buffer
設置jsp網頁的緩沖區大小,默認為”8k”,如果設置為”none”,則表示不使用緩沖,所有的響應輸出都將被PrintWriter直接寫到ServletResponse中。
l isTreadSafe
定義當前頁面是否支持線程安全。如果為”true”,則該頁面可能同時收到jsp引擎發出的多個請求,反之,jsp引擎會對收到的請求進行排隊,當前頁面在同一時刻只能處理一個請求。默認為”true”。
l info
設置頁面的文本信息,可以通過Servlet.getServletInfo()的方法獲得該字符串。
l errorPage
定義指向另一個jsp頁面的URL。當頁面出現一個沒有被捕獲的異常時,錯誤信息將以throw語句拋出,而被設置為錯誤信息網頁的jsp頁面,將利用exception隱含對象,取得錯誤信息。
默認沒有錯誤處理頁面。
l isErrorPage
設置此jsp網頁是否為錯誤處理頁面。默認值為”false”。當設置為”true”時,jsp頁面將可存取隱含的exception對象,并通過該對象取得從發生錯誤之網頁所傳出的錯誤信息。取得錯誤信息的語法如下:
<% =exception.getMessage()%>
2 一個頁面錯誤處理的例子
產生錯誤的頁面文件為MakeError.jsp,處理錯誤的頁面文件為ErrorPage.jsp,它們的源程序如下:
MakeError.jsp
- <%@ page errorPage="ErrorPage.jsp"%>
- <html>
- <head>
- <title>產生錯誤頁面</title>
- </head>
- <body>
- <%
- int i=8,j=0;
- out.println(ij);
- %>
- </body>
- </html>
- ErrorPage.jsp
- <%@ page isErrorPage="true"%>
- <html>
- <head>
- <title>錯誤處理頁面</title>
- </head>
- <body>
- <font color=red>
- 錯誤原因:<%=exception.getMessage()%>
- </font>
- </body>
- </html>
運行程序MakeError.jsp的結果如下:
2. include指令
使用include指令可以把其他的文本文件加入到當前的jsp頁面,格式如下:
<%@ include file=”header.inc”%>
如此,則在當前頁面中加入header.inc源代碼然后再編譯整個文件。
可以使用include指令把一個頁面分成不同的部分,最后合成一個完整的文件,使用jsp的include指令有助于實現jsp頁面的模塊化。
3. taglib指令
(略)
二. 腳本元素
JSP規格提供了四種類型的腳本元素,包括:
l 聲明
l 表達式
l 腳本
l 注釋
下面分別對它們進行詳細敘述。
1. 聲明元素
聲明用于定義jsp頁面中的變量與函數,這些經過定義的變量和函數,將成為Servlet類的屬性與方法(關于Servlet請參看后文)。聲明并不會產生任何的數據輸出,聲明時可同時設置初始值,提供給其他的聲明、表達式或腳本使用。
聲明的語法格式為:
- <%!
- //聲明語句
- %>
- 舉例:
- <%!
- //此處定義的變量將成為此jsp頁面的全局變量
- int i = 0;
- static int j=100;
- String s = “注意”;
- %>
- <%!
- //此處定義的函數將成為此jsp頁面的公共函數
- Public int square(int i)
- {
- return(i*i);
- }
- %>
2 jspInit函數與jspDestroy函數
若要在jsp頁面開始執行時進行某些數據的初始化,可以利用jspInit函數完成。此函數將在jsp頁面被執行時調用,且當jsp頁面重新整理時,并不會被再度執行。當關閉服務器時,jspDestroy函數將被執行,可以利用該函數進行數據的善后處理工作。下面舉個簡單的例子說明,文件InitDes.jsp代碼如下:
- <%@ page contentType="text/html; charset=GB2312"%>
- <%!
- public void jspInit()
- {
- System.out.println("jspInit is called!");
- }
-
- public void jspDestroy()
- {
- System.out.println("jspDestroy is called!");
- }
- %>
- <HTML>
- <HEAD><TITLE>jspInit函數與jspDestroy函數的使用</TITLE></HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>jspInit函數與jspDestroy函數的使用</FONT>
- </CENTER>
- <HR><BR>
- </BODY>
- </HTML>
首次執行此頁面時,Resin服務器輸出如下:
Resin 1.2.2 -- Tue Jan 16 09:53:18 PST 2001
http listening to *:8080
srun listening to 127.0.0.1:6802
jspInit is called!
刷新此頁面數次后,Resin服務器輸出仍然如上。
此時,如果關閉服務器,則輸出如下:
Resin 1.2.2 -- Tue Jan 16 09:53:18 PST 2001
http listening to *:8080
srun listening to 127.0.0.1:6802
jspInit is called!
closing server
jspDestroy is called!
由此,我們得到啟發,在數據庫的開發過程中,可以利用jspInit函數來進行數據庫的連接工作,用jspDestroy函數來進行數據庫的關畢工作。下面以一個分頁顯示數據庫內容的程序為例子,讓讀者進一步體會jspInit與jspDestroy的功用與好處。
在Pages.jsp這個分頁程序中,我們把數據庫連接的動作寫在jspInit函數中,這樣,每一次重新整理頁面時,就可以避免重新執行數據庫的連接動作。如下:
- <%@ page contentType="text/html; charset=GB2312"
- import="java.sql.*"%>
- <%!
- int PageSize = 2; //設置每張網頁顯示兩筆記錄
- int ShowPage = 1; //設置欲顯示的頁數
- int RowCount = 0; //ResultSet的記錄筆數
- int PageCount = 0; //ResultSet分頁后的總頁數
- Connection con = null;
- Statement stmt = null;
- ResultSet rs = null;
-
- public void jspInit() //執行數據庫與相關數據的初始化
- {
- try
- {
- Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
- //載入驅動程序類別
-
- con = DriverManager.getConnection("jdbc:odbc:test");
- //建立數據庫鏈接
-
- stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
- ResultSet.CONCUR_READ_ONLY);
- //建立Statement對象, 并設置記錄指標類型為可前后移動
-
- rs = stmt.executeQuery("SELECT * FROM products");
- //建立ResultSet(結果集)對象,并執行SQL語句
-
- rs.last(); //將指標移至最后一筆記錄
-
- RowCount = rs.getRow(); //取得ResultSet中記錄的筆數
-
- PageCount = ((RowCount % PageSize) == 0 ?
- (RowCountPageSize) : (RowCountPageSize)+1);
- //計算顯示的頁數
- }
- catch(Exception ex)
- {
- System.out.println(ex.toString());
- }
- }
-
- public void jspDestroy() //執行關閉各種對象的操作
- {
- try
- {
- rs.close(); //關閉ResultSet對象
- stmt.close(); //關閉Statement對象
- con.close(); //關閉數據庫鏈接對象
- }
- catch(Exception ex)
- {
- System.out.println(ex.toString());
- }
- }
- %>
- <HTML>
- <HEAD>
- <TITLE>記錄的分頁顯示</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>記錄的分頁顯示</FONT>
- </CENTER>
- <HR>
- <P></P>
- <CENTER>
- <%
- String ToPage = request.getParameter("ToPage");
-
- //判斷是否可正確取得ToPage參數,
- //可取得則表示JSP網頁應顯示特定分頁記錄的語句
- if(ToPage != null)
- {
- ShowPage = Integer.parseInt(ToPage); //取得指定顯示的分頁頁數
-
- //下面的if語句將判斷用戶輸入的頁數是否正確
- if(ShowPage > PageCount)
- { //判斷指定頁數是否大于總頁數, 是則設置顯示最后一頁
- ShowPage = PageCount;
- }
- else if(ShowPage <= 0)
- { //若指定頁數小于0, 則設置顯示第一頁的記錄
- ShowPage = 1;
- }
- }
-
- rs.absolute((ShowPage - 1) * PageSize + 1);
- //計算欲顯示頁的第一筆記錄位置
- %>
- <H3>目前在第<FONT SIZE = 4 COLOR = red>
- <%= ShowPage %></FONT>頁, 共有
- <FONT SIZE = 4 COLOR = red>
- <%= PageCount %></FONT>頁</H3>
- <P></P>
- <%
- //利用For循環配合PageSize屬性輸出一頁中的記錄
- for(int i = 1; i <= PageSize; i++)
- {
- %>
- <TABLE border=1 bordercolor=RoyalBlue bgcolor=LightBlue>
- <TR><TD bgcolor=LightYellow width= 100>
- <B>商品名</B></TD>
- <TD width= 100><B><%= rs.getString("product_name") %>
- </B></TD>
- <TD bgcolor=LightYellow width= 100>
- <B>價格</B></TD>
- <TD width= 100><B><%= rs.getInt("price") %>
- </B></TD>
- <TD bgcolor=LightYellow width= 100>
- <B>描述</B></TD>
- <TD width= 100><B><%= rs.getString("description") %>
- </B></TD>
- </TR>
- </TABLE><BR>
- <%
- //下面的if判斷語句用于防止輸出最后一頁記錄時,
- //將記錄指標移至最后一筆記錄之后
- if(!rs.next()) //判斷是否到達最后一筆記錄
- break; //跳出for循環
- }
- %>
- <TABLE>
- <TR valign=baseline align=center>
- <%
- //判斷目前所在分頁是否為第一頁,
- //不是則顯示到第一頁與上一頁的超鏈接
- if(ShowPage != 1)
- {
- //下面建立的各超鏈接將鏈接至自己,
- //并將欲顯示的分頁以ToPage參數傳遞給自己
- %>
- <TD Width=150>
- <A Href=Pages.jsp?ToPage=<%= 1 %>>到第一頁</A>
- </TD>
- <TD Width=150>
- <A Href=Pages.jsp?ToPage=<%= ShowPage - 1 %>>到上一頁</A>
- </TD>
- <%
- }
-
- //判斷目前所在分頁是否為最后一頁,
- //不是則顯示到最后一頁與下一頁的超鏈接
- if(ShowPage != PageCount)
- {
- //下面建立的各超鏈接將鏈接至自己,
- //并將欲顯示的分頁以ToPage參數傳遞自己
- %>
- <TD Width=150>
- <A Href=Pages.jsp?ToPage=<%= ShowPage + 1%>>到下一頁</A>
- </TD>
- <TD Width=150>
- <A Href=Pages.jsp?ToPage=<%= PageCount %>>到最后一頁</A>
- </TD>
- <%
- }
- %>
- <TD Width=150>
- <FORM action=Pages.jsp method=POST>
- 到
- <!--
- 供用戶輸入欲查看頁數的文字方塊, 預設值為目前所在的分頁,
- 當用戶在此文字方塊中完成數據輸入后按下 Enter 即可將數據送出,
- 相當于按下Submit按鈕, 因此此表單中將省略Submit按鈕
- -->
- <INPUT type="text" name=ToPage style="HEIGHT: 25px; WIDTH: 40px"
- value=<%= ShowPage%> > 頁
- </FORM></TD></TR>
- </TABLE>
- </CENTER>
- </BODY>
- </HTML>
執行后,結果如下圖:
2. 表達式元素
表達式是一個簡化了的out.println語句。
表達式的語法格式為:
<%=//要輸出的數據%>
舉例:
<%=square(5)%>
3. 腳本元素
腳本是java程序的一段代碼,只要符合java語法的語句都可以寫在這里,它是在請求時期執行的,它可以使用jsp頁面所定義的變量、方法、表達式或JavaBeans。
腳本的語法格式為:
- <%
- //java代碼
- %>
- 舉例:
- <%
- if(age<18)
- {
- out.println(“你是未成年人!!!!”);
- }
- else
- {
- out.println(“你已經成年了!!!!”);
- }
- %>
4. 注釋元素
用來對程序進行說明注釋。注釋大體有下列三種格式:
<!—客戶端注釋à
<!--<%=客戶端動態注釋%>-->
<%--服務器端注釋--%>
三. 標準動作元素
標準動作元素用于執行一些常用的JSP頁面動作,例如:將頁面轉向、使用JavaBean、設置JavaBean的屬性等。在JSP中,標準動作元素共有以下幾種:
l <jsp:param>
l <jsp:include>
l <jsp:forward>
l <jsp:plugin>
l <jsp:useBean>
l <jsp:setProperty>
l <jsp:getProperty>
其中<jsp:useBean>、<jsp:setProperty>、<jsp:getProperty>這三個是專門用來操作JavaBeans的。
下面分別介紹它們。
1. <jsp:param>
<jsp:param>動作用于傳遞參數,必須配合<jsp:include>、<jsp:forward>、<jsp:plugin>動作一起使用。
語法格式:
<jsp:param name = “name1” value = “value1”/>
2. <jsp:include>
<jsp:include>動作用于動態加載HTML頁面或者JSP頁面。
語法格式:
<jsp:include page = “網頁路徑”>
<jsp:param name = “name1” value = “value1”/>
<jsp:param name = “name2” value = “value2”/>
<jsp:include/>
在jsp頁面中,可以利用下面的語法取得返回的參數:
request.getParameter(“name1”);
若不傳遞參數時,則語法格式如下:
<jsp:include page = “網頁路徑”/>
舉例:
a.jsp頁面代碼如下:
- <jsp:include page = "b.jsp">
- <jsp:param name = "name1" value = "value1"/>
- <jsp:param name = "name2" value = "value2"/>
- </jsp:include>
b.jsp頁面代碼如下:
名字1、;<%=request.getParameter("name1")%>
<hr color=red>
名字2、;<%=request.getParameter("name2")%>
執行結果如下:
“include標準動作”和“include指令”的差別在于:“include標準動作”包含的頁面在運行時被加入,而“include指令”在編譯時就被加入了。
3. <jsp:forward>
<jsp:forward>動作用于將瀏覽器顯示的頁面導向到另一個HTML頁面或者jsp頁面。
語法格式:
<jsp:forward page = “網頁路徑”/>
當然,<jsp:forward>動作中也可以加入<jsp:param>參數,其設置和獲得參數的方法與<jsp:include>類似。
4. <jsp:plugin>
<jsp:plugin>動作用于加載applet,用途與HTML語法中的<Applet>及<Object>標記相同。該動作是在客戶端執行的,這里就不作介紹了。
5. <jsp:useBean>
(見后文的“JavaBeans”的使用)
6. <jsp:setProperty>
(見后文的“JavaBeans”的使用)
7. <jsp:getProperty>
(見后文的“JavaBeans”的使用)
四. 內置對象
在jsp頁面中有一些已經完成定義的對象,稱之為內置對象。這些對象可以不經過定義就直接使用,因為它們是由jsp頁面自己定義的。
jsp程序常用的內建對象有如下幾個:request、response、out、session、pageContext、application、config、page、exception。你可以在jsp頁面中直接使用它們,用以加強jsp程序的功能。
下面分別介紹它們。
1. request
與request相聯系的是HttpServletRequest類。通過getParameter方法可以獲得相應的參數值。
2. response
與response相聯系的是HttpServletResponse類。表示Web頁面針對請求的應答。
3. out
與out相聯系的是PrintWrite類。可以使用此對象將內容輸出到頁面中。
4. session
與session相聯系的是HttpSession類。用來傳遞客戶的會話內容。
5. pageContext
與pageContext相聯系的是pageContext類。用它能方便的訪問本頁面中設置的共享數據。
6. application
與application相聯系的是ServletContext類。用它能夠實現應用程序級別的數據共享。
7. config
與config相聯系的是ServletConfig類。用來在jsp頁面范圍內處理jsp配置。
8. page
代表jsp頁面編譯成的Servlet實例,一般不用。
9. exception
與exception相聯系的是Throwable類。用來捕獲jsp執行時拋出的異常。
五. JavaBeans的使用
JavaBeans是運行于java虛擬機上的100%的純java組件,它的概念描述很類似于Microsoft的COM組件概念。
JavaBeans傳統的應用在于可視化領域,如AWT下的應用。其實,基于AWT的任何java程序已經是一個Bean,完全可以把它當作一個組件來使用。
現在,JavaBeans更多的應用在不可視化領域,它在服務器端應用方面表現出了越來越強的生命力。不可視化的JavaBeans在JSP程序中用來封裝事務邏輯,可以很好的實現業務邏輯和前臺程序的分離,使得系統具有更好的健壯性和靈活性。
JavaBeans描述了JDK1.1以前的java所沒有的東西,因此,運行JavaBeans最小的需求是JDK1.1或者以上的版本。
1. JavaBeans在JSP中的基本使用格式
l 在JSP中調用JavaBeans的格式
//加載Bean
<jsp:useBean id = “名稱” scope = “有效范圍” class = “Bean類位置”/>
//設定Bean屬性(兩種方法)
//方法一:“標簽設定”
<jsp:setProperty name = “名稱” property = “屬性” value = “值”/>
//方法二:“方法設定(用于java程序中)”
Bean對象名稱.set屬性(值)
//獲取Bean屬性(兩種方法)
//方法一:“標簽獲取”
<jsp:getProperty name = “名稱” property = “屬性”/>
//方法二:“方法獲取(用于java程序中)”
Bean對象名稱.get屬性()
l JavaBean編寫的格式
//定義Bean類所屬于的包
- package 包名
-
- //定義為公開等級的類,并且類名稱與源代碼文件名相同
- public class類名
- {
- //Bean類的屬性,其等級定義為private
- private 數據類型 屬性名
-
- //用來初始化的構造函數
- //Bean的構造函數無輸入參數
- public 類名
- { }
-
-
- //以setXXX函數,作為設定Bean類屬性的接口
- public void set屬性名稱(數據類型 參數)
- {
- this.屬性 = 參數
- }
-
- //以getXXX函數,作為取得Bean類屬性的接口
- public void get屬性名稱()
- {
- return this.屬性
- }
- }
2 一個簡單的使用JavaBeans的例子
Bean文件LoginData.java的源代碼如下:
- package j2ee.jsp;
- //定義Bean所屬的包
-
- public class LoginData
- {
- //Bean屬性
- private String Name = "";
- private String Pwd = "";
-
- public LoginData() //構造函數
- {
- }
-
- //以下為設定Bean屬性的方法
- public void setLoginName(String name)
- { this.Name = name; }
- public void setPassword(String pwd)
- { this.Pwd = pwd; }
-
- //以下為取得Bean屬性的方法
- public String getLoginName()
- { return this.Name; }
- public String getPassword()
- { return this.Pwd; }
- }
調用Bean的jsp文件UseBean.jsp源程序如下:
- <%@ page contentType="text/html; charset=GB2312" %>
- <HTML>
- <HEAD>
- <TITLE>使用Beans</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>使用Beans</FONT>
- </CENTER>
- <HR>
- <P></P>
- <H2>
- <jsp:useBean id="login" scope="application"
- class="j2ee.jsp.LoginData"/>
- <jsp:setProperty name="login"
- property="loginName" value="最后的決定"/>
- <%
- login.setPassword("123456"); //調用Bean對象的方法, 設定屬性
- %>
-
- <Font color = red>LoginName</Font>屬性值為
- <Font color = blue>
- <jsp:getProperty name="login" property="loginName"/>
- </Font><BR>
- <Font color = red>Password</Font>屬性值為
- <Font color = blue>
- <%--以調用Bean對象方法的方式取得屬性--%>
- <%= login.getPassword() %></Font>
- </BODY>
- </HTML>
運行結果如下:
在前面的使用中,有兩點值得注意:
(1) Bean中各個方法名的“命名規則及大小寫”與調用Bean時的“方法名規則及大小寫”之間的對應關系需要注意。
(2) Beans的存放目錄將隨選用服務器的不同而不同。以resin服務器而言,Beans默認定義存放在application-programme\WEB-INF\classes子目錄中。
2. scope范圍的具體設定
JavaBeans可以定義四種生命周期?D?Dpage、request、session與application,將分別運用pageContext、request、session、application四種對象的setAttribute方法,將JavaBeans對象保存在該對象中。下面分別說明:
l Page的有效范圍僅僅涵蓋使用JavaBeans的頁面,一旦你離開此頁面,JavaBeans對象的實體也將隨之消失。
l Request的有效范圍僅及于使用JavaBeans的請求而已,一旦你結束該頁面的請求,JavaBeans對象的實體也將隨之消失。
l Session的有效范圍涵蓋了整個用戶會話時期。在用戶會話期間,JavaBeans對象的實體均不會消失。當用戶會話結束時,JavaBeans對象的實體才會消失。
l Application的有效范圍則涵蓋了整個應用程序時期。在應用程序期間,JavaBeans對象的實體均不會消失。只有當應用程序結束時,JavaBeans對象的實體才會消失。
下面,舉一個簡單的例子,對Request與Session兩種生命周期做具體的演示。
Bean文件counter.java的源代碼如下:
- package j2ee.jsp;
- public class counter
- {
- private int count = 0;
-
- public void setCount(int c)
- {
- this.count = c;
- }
-
- public int getCount()
- {
- this.count++;
- return this.count;
- }
- }
Request實例
兩個jsp文件b1.jsp與b2.jsp代碼分別如下:
b1.jsp
- <jsp:useBean id="counter" scope="request" class="j2ee.jsp.counter"/>
-
- <%
- counter.setCount(100);
- %>
-
- <jsp:forward page="b2.jsp"/>
- b2.jsp
- <jsp:useBean id="counter" scope="request" class="j2ee.jsp.counter"/>
-
- <%
- out.println(counter.getCount());
- %>
運行結果如下:
Session實例
兩個jsp文件c1.jsp與c2.jsp代碼分別如下:
c1.jsp
<jsp:useBean id="counter" scope="session" class="j2ee.jsp.counter"/>
<%
out.println(counter.getCount());
%>
<a href="c2.jsp" target="_blank">c2.jsp</a>
c2.jsp
<jsp:useBean id="counter" scope="session" class="j2ee.jsp.counter"/>
<%
out.println(counter.getCount());
%>
運行結果如下:
3. session事件的運用
在jsp頁面中,將Bean對象保存至session對象時,可以定義Bean響應HttpSessionBindingEvent事件。當Bean對象加入session、Bean從session中刪除以及session對象終止時,將會觸發此事件。因此,我們可以利用這兩個事件,執行數據起始、善后的工作。
由此,我們可以想到,把jsp頁面中最耗費服務器資源的數據庫連接工作放入HttpSessionBindingEvent事件中。當一個會話開始時,建立一個“數據庫連機”,隨后的整個會話過程中,所有與數據庫相關的操作均使用這一個“連機”,這樣,就避免了每執行一次數據庫操作就產生一個數據庫連機的巨大消耗。當此會話結束時,再關閉釋放這個“數據庫連機”。
如果要Bean對象響應HttpSessionBindingEvent事件,則該Bean對象必須實現HttpSessionBindingListener接口,并且定義響應會話開始的valueBound方法以及響應會話結束的valueUnbound方法。
現在,我們來實做一個例子,首先,建立一個“用來建立會話級別數據庫聯機”的Bean文件DBCon.java,它的源代碼如下所示:
編譯這個Bean源文件。注意,編譯前要設定好classpath的路徑,使得它所包含的類庫中有javax.servlet.http.*包。
然后,建立兩個用來測試此Bean的jsp頁面文件DBBean1.jsp與DBBean2.jsp,它們的程序代碼差不多,都是用來顯示數據庫內容的,現在就只列出DBBean1.jsp的源文件,如下:
- <%@ page contentType="text/html; charset=GB2312"
- import="java.sql.*"%>
- <HTML>
- <HEAD>
- <TITLE>利用Bean對象建立數據庫鏈接</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>
- 利用Bean對象建立數據庫鏈接
- </FONT>
- </CENTER>
- <HR>
- <P></P>
-
- <CENTER>
- <%--起始建立數據庫鏈接的Bean對象--%>
- <jsp:useBean id="ConBean" scope="session"
- class="j2ee.jsp.DBCon"/>
- <%
- Connection con = ConBean.getConnection();
- //從Bean對象取得已完成建立的數據庫鏈接
-
- Statement stmt = con.createStatement();
- //建立Statement對象
-
- ResultSet rs = stmt.executeQuery("SELECT product_name, price FROM products");
- //建立ResultSet(結果集)對象,并執行SQL敘述
- %>
- <TABLE bgcolor=DodgerBlue>
- <TR bgcolor=SkyBlue>
- <TD><B>書 名</B></TD><TD><B>價 格</B></TD>
- </TR>
- <%
- //利用while循環將數據表中的記錄列出
- while (rs.next())
- {
- %>
- <TR bgcolor=LightGoldenrodYellow>
- <TD><B><%= rs.getString("product_name") %></B></TD>
- <TD><B><%= rs.getString("price") %></B></TD>
- </TR>
- <%
- }
-
- rs.close(); //關閉記錄集
- stmt.close(); //關閉Statement對象
- %>
- </TABLE>
- </CENTER>
- <a href="DBBean2.jsp">DBBean2.jsp</a>
- </BODY>
- </HTML>
首次運行DBBean1.jsp文件時,瀏覽器顯示如下:
Resin服務器的輸出如下:
Resin 1.2.2 -- Tue Jan 16 09:53:18 PST 2001
http listening to *:8080
srun listening to 127.0.0.1:6802
BulidConnection()方法被調用
會話級別的數據庫連接已經建立!!!
由此可知,當DBBean1.jsp文件中定義DBCon對象的生命周期為session時,就觸發了HttpSessionBindingEvent事件,并調用了valueBound方法,建立了“會話級別的數據庫聯機”。
然后,我們再在DBBean2.jsp等多個頁面中跳轉,Resin服務器的輸出仍然如上所示,這就表明了,同一會話中的各個頁面均使用了同一個數據庫聯機,所以才沒有建立新的聯機。
當關閉服務器的時候,輸出數據如下:
Resin 1.2.2 -- Tue Jan 16 09:53:18 PST 2001
http listening to *:8080
srun listening to 127.0.0.1:6802
BulidConnection()方法被調用
會話級別的數據庫連接已經建立!!!
closing server
會話級別的數據庫連接已經關閉!!!
此時,會話結束,觸發了HttpSessionBindingEvent事件,并調用了valueUnbound方法,關閉了先前建立的會“話級別的數據庫聯機”。
4. Bean的保存與讀取
到目前為止,我們所使用的Bean對象均能于建立該對象的頁面中使用,而無法將執行的結果保存下來供下次頁面執行使用。現在,我們就來討論一下Bean的保存的問題。
若要某個Bean對象保存進文件,就必須使該Bean可串行化,即該Bean必須實現java.io.Serializable接口。
另外,如果要把Bean對象保存進文件,或是從文件中讀取Bean對象,需要用到四個特別的java.io包中的對象,它們是FileOutputStream、ObjectOutputStream、FileInputStream、ObjectInputStream,其中的前兩個使用來保存Bean對象的,后兩個則是用來讀取Bean對象的。具體的使用方法,我們還是通過例子來說明吧!!!
2 Bean對象的保存與讀取
我們將要建立如下幾個文件,它們的用途及說明如下表:
文件名稱 說明 用途
SaveBean.java 定義要保存進文件的Bean對象,此對象將實現Serializable接口。 此Bean中將保存姓名、性別、年齡、生日、愛好這五個個人信息。
BeanSaver.java 定義將Bean對象保存進文件,或是從文件中讀取Bean對象的BeanSaver對象。 用來保存或是讀取Bean對象。
SaveBean.jsp 建立SaveBean對象,并設定相應的個人信息,然后,把此對象寫入文件。 建立SaveBean對象,并利用BeanSaver對象把SaveBean對象寫入文件SB.ser。
LoadBean.jsp 利用BeanSaver對象,從文件中取得SaveBean對象。 從文件SB.ser中取得SaveBean對象,然后將其內容,即個人信息顯示在頁面上。
兩個Bean的源文件如下:
SaveBean.java
編譯上面的兩個源文件,產生j2ee.jsp.SaveBean.class與j2ee.jsp.BeanSaver.class。
兩個jsp頁面的程序代碼如下:
SaveBean.jsp
- <%@ page contentType="text/html; charset=GB2312" %>
- <HTML>
- <HEAD>
- <TITLE>Bean對象的保存與取得</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>Bean對象的保存</FONT>
- </CENTER>
- <HR>
-
- <jsp:useBean id="info" scope="page"
- class="j2ee.jsp.SaveBean"/>
- <%
- //調用Bean對象的方法, 設定屬性
- info.setName("最后的決定");
- info.setSex("男");
- info.setAge(22);
- info.setBirth("1981-9-10");
- info.setLove("流行音樂,武俠劇,唱歌,互聯網,漂亮mm");
- %>
-
-
- <jsp:useBean id="saver" scope="page"
- class="j2ee.jsp.BeanSaver"/>
- <%
- //保存對象
- saver.save(info,"D:\\java\\SB.ser");
- %>
-
- <H2>
- SaveBean對象已經保存完畢
- </H2>
-
- </BODY>
- </HTML>
- LoadBean.jsp
- <%@ page contentType="text/html; charset=GB2312"
- import="j2ee.jsp.*"%>
- <HTML>
- <HEAD>
- <TITLE>Bean對象的保存與取得</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <FONT SIZE = 5 COLOR = blue>Bean對象的取得</FONT>
- </CENTER>
- <HR>
- <P></P>
- <jsp:useBean id="saver" scope="page"
- class="BeanSaver"/>
- <jsp:useBean id="info" scope="page"
- class="SaveBean"/>
- <%
- //將對象設定至pageContext對象中
- pageContext.setAttribute("info", saver.load("d:\\java\\SB.ser"));
-
- //若欲已調用Bean對象方法的方式運用Bean時, 需使用此敘述
- info = (SaveBean) pageContext.getAttribute("info");
- %>
-
- 個人資料<br>
- <Font color = red>姓名:</Font>
- <Font color = blue>
- <%= info.getName() %></Font><BR>
- <Font color = red>性別:</Font>
- <Font color = blue>
- <%= info.getSex() %></Font><BR>
- <Font color = red>年齡:</Font>
- <Font color = blue>
- <%= info.getAge() %></Font><BR>
- <Font color = red>生日:</Font>
- <Font color = blue>
- <%= info.getBirth() %></Font><BR>
- <Font color = red>愛好:</Font>
- <Font color = blue>
- <%= info.getLove() %></Font><BR>
-
- </BODY>
- </HTML>
首先在運行SaveBean.jsp,結果如下:
然后運行LoadBean.jsp,顯示如下:
至此,恭喜你已經學會了JavaBeans所有最核心的應用了,jsp強大的功用也已經展現在了你的眼前,快去構建威力無窮的Web應用吧,祝你一路順風:) 。
六. JSP中的文件操作
自從有了數據庫系統以后,文件的操作似乎變得不再那么重要了,很多大型的應用系統也往往愿意把數據的存取工作交給數據庫而不是文件。就筆者自身的體會是,數據庫的確在很多時候可以替代文件,但在某些特定的應用上使用文件可以得到更佳的應用效果和方便的操作特性,而且在操作少量數據的存取時,文件不但方便保存攜帶,更不像數據庫那樣運行時需要消耗大量的服務器資源。因此,筆者認為,文件的操作仍然是一個重點。
要在JSP中操作文件,大體上需要用到java.io.*包中的數個類及其相應的方法,很多時候實現同一功能都有數種不同的方法。在此,筆者不想在各個類的基本定義格式及其相應方法的說明上多費筆墨,而是直接把一個集合了眾多文件操作功能的Bean呈現在各位讀者面前。使用此Bean,可以進行絕大多數的文件操作,至于其中具體到某個類、某個方法的操作原理及其細節,請各位參看相關資料。
下表列出了CtrlFile類的所有公開方法及其說明:
方法名 用途說明
createNewFile 建立一個新的文件
deleteFile 刪除文件
createNewDir 建立一個新的目錄
deleteDir 刪除目錄
fileLength 測量文件的長度
isFile 判斷是不是文件
isDir 判斷是不是目錄
readLine 讀取文件的第一行
readAll 讀取整個文件的內容
writeLine 把數據寫入文件
writeAppend 把數據追加入文件
- /*
- * 文件名:CtrlFile.java
- *
- * 類名:CtrlFile
- *
- * 所屬包:j2ee.jsp
- *
- * 導入包:import java.io.*;
- *
- * 作者:楊??
- *
- * 創建時間:2003.12.10
- *
- * 用途描述:對目錄、文件進行讀、寫、新建、刪除等操作。
- *
- * 版本號:1.0
- *
- */
-
- package j2ee.jsp;
-
- import java.io.*;
-
- //注意:各個函數中要到的參數path均為實際路徑.
-
- public class CtrlFile
- {
-
- /**
- * 方法名:FileName
- * 級別:private
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return String 文件名
- * @throws (無)
- * 作用:從文件的實際路徑中取出文件名
- */
- private String FileName(String path)
- {
- int pos = path.lastIndexOf('\\');
- String FileName = path.substring(pos+1);
- return(FileName);
- }
-
- /**
- * 方法名:PathName
- * 級別:private
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return String 路徑
- * @throws (無)
- * 作用:從文件的實際路徑中取出路徑
- */
- private String PathName(String path)
- {
- int pos = path.lastIndexOf('\\');
- String PathName = path.substring(0,pos);
- return(PathName);
- }
-
- /**
- * 方法名:createNewFile
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @param boolean 是否覆蓋
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:建立一個新的文件
- */
- public void createNewFile(String path,boolean rebuild) throws Exception
- {
- String FileName = new String(FileName(path));
- String PathName = new String(PathName(path));
- File f = new File(PathName,FileName);
- if(rebuild==true)
- {
- if(f.exists()==true)
- {
- f.delete();
- }
- f.createNewFile();
- }
- else
- {
- if(f.exists()==false)
- {
- f.createNewFile();
- }
- }
-
- }
-
- /**
- * 方法名:deleteFile
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:刪除文件
- */
- public void deleteFile(String path) throws Exception
- {
- String FileName = new String(FileName(path));
- String PathName = new String(PathName(path));
- File f = new File(PathName,FileName);
- f.delete();
- }
-
- /**
- * 方法名:createNewDir
- * 級別:public
- * @param String 目錄的實際路徑
- * @param boolean 是否覆蓋
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:建立一個新的目錄
- */
- public void createNewDir(String path,boolean rebuild) throws Exception
- {
- File d = new File(path);
- if(rebuild==true)
- {
- if(d.exists()==true)
- {
- d.delete();
- }
- d.mkdir();
- }
- else
- {
- if(d.exists()==false)
- {
- d.mkdir();
- }
- }
- }
-
- /**
- * 方法名:deleteDir
- * 級別:public
- * @param String 目錄的實際路徑
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:刪除目錄
- */
- public void deleteDir(String path) throws Exception
- {
- File d = new File(path);
- d.delete();
- }
-
- /**
- * 方法名:fileLength
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return long 文件的長度
- * @throws Exception 如果發生錯誤
- * 作用:測量文件的長度
- */
- public long fileLength(String path) throws Exception
- {
- String FileName = new String(FileName(path));
- String PathName = new String(PathName(path));
- File f = new File(PathName,FileName);
- long fileLength = f.length();
- return(fileLength);
- }
-
- /**
- * 方法名:isFile
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return boolean 是否文件
- * @throws Exception 如果發生錯誤
- * 作用:用來判斷是不是文件
- */
- public boolean isFile(String path) throws Exception
- {
- String FileName = new String(FileName(path));
- String PathName = new String(PathName(path));
- File f = new File(PathName,FileName);
- boolean isFile = f.isFile();
- return(isFile);
- }
-
- /**
- * 方法名:isDir
- * 級別:public
- * @param String 目錄的實際路徑
- * @return boolean 是否目錄
- * @throws Exception 如果發生錯誤
- * 作用:用來判斷是不是目錄
- */
- public boolean isDir(String path) throws Exception
- {
- File d = new File(path);
- boolean isDir = d.isDirectory();
- return(isDir);
- }
-
- /*-------------------------以上是針對文件的操作-------------------------*/
-
- /*-------------------------以下是針對文件的讀寫-------------------------*/
-
- /**
- * 方法名:readLine
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return String 文件中第一行的內容
- * @throws Exception 如果發生錯誤
- * 作用:用來讀取文件的第一行
- */
- public String readLine(String path) throws Exception
- {
- FileReader fr = new FileReader(path);
- BufferedReader br = new BufferedReader(fr);
- String Line = br.readLine();
- return(Line);
- }
-
- /**
- * 方法名:readAll
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @return String 文件中的所有內容
- * @throws Exception 如果發生錯誤
- * 作用:用來讀取整個文件的內容
- */
- public String readAll(String path) throws Exception
- {
- FileReader fr = new FileReader(path);
- BufferedReader br = new BufferedReader(fr);
- String txt = new String();
- String Line = new String();
- Line = br.readLine();
- while(Line!=null)
- {
- txt = txt + Line;
- Line = br.readLine();
- }
- br.close();
- fr.close();
- return(txt);
- }
-
- /**
- * 方法名:writeLine
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @param String 要寫入文件中的內容
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:用來把數據寫入文件
- */
- public void writeLine(String path,String content) throws Exception
- {
- FileWriter fw = new FileWriter(path);
- fw.write(content);
- fw.close();
- }
-
- /**
- * 方法名:writeAppend
- * 級別:public
- * @param String 文件的實際路徑 , 即:路徑 + 文件名
- * @param String 要寫入文件中的內容
- * @return (無)
- * @throws Exception 如果發生錯誤
- * 作用:用來把數據追加入文件
- */
- public void writeAppend(String path,String content) throws Exception
- {
- FileWriter fw = new FileWriter(path,true);
- PrintWriter pw = new PrintWriter(fw);
- pw.print(content + "\n");
- pw.close();
- fw.close();
- }
- }
七. JSP運行原理剖析
談到JSP的運行原理,就不得不談到Servlet了。雖然按照Sun的觀點,將來是要用JSP替代Servlet的,但JSP是在Servlet的基礎上發展起來的,要深刻理解JSP的運行原理,就一定需要Servlet的相關知識。在閱讀本節前,筆者假設你已經至少對Servlet有了基本的概念理解。OK,不再多說廢話了,Let’s go!!
Servlet技術的出現時間很早,是當時為了Java的服務器端應用而開發的。大家都知道Applet是應用小程序,Servlet就是服務器端小程序了。但在Microsoft公司的ASP技術出現后,使用Servlet進行響應輸出時一行行的輸出語句就顯得非常笨拙,對于復雜布局或者顯示頁面更是如此。JSP就是為了滿足這種需求在Servlet技術之上開發的。可見,JSP和Servlet之間有著內在的血緣關系,在學習JSP時,如果能夠抓住這種聯系,就能更深刻地理解JSP的運行機理,達到事半功倍的效果。
JSP頁面的請求響應過程如下圖:
下面筆者舉例說明:
HelloWorld.jsp代碼清單如下:
- <%@ page contentType="text/html; charset=GB2312"%>
- <HTML>
- <HEAD>
- <TITLE>Hello World</TITLE>
- </HEAD>
- <BODY>
- <%="Hello World!!!!!!"%>
- </BODY>
- </HTML>
這個文件存放在D:\java\resin1.2.2\webapps中,通過瀏覽器請求執行HelloWorld.jsp:
http://127.0.0.1:8080/helloworld.jsp。
服務器resin接到這個請求后,調出jsp引擎,首先將HelloWorld.jsp解析成Servlet存入D:\java\resin1.2.2\work\_jsp目錄,文件名是_helloworld__jsp.java,此Servlet的源代碼如下:
- /*
- * JSP generated by Resin 1.2.2 -- Tue Jan 16 09:53:18 PST 2001
- */
-
- package _jsp;
- import java.io.*;
- import javax.servlet.*;
- import javax.servlet.jsp.*;
- import javax.servlet.jsp.tagext.*;
- import javax.servlet.http.*;
-
- public class _helloworld__jsp extends com.caucho.jsp.JavaPage{
-
- public void
- _jspService(javax.servlet.http.HttpServletRequest request,
- javax.servlet.http.HttpServletResponse response)
- throws java.io.IOException, javax.servlet.ServletException
- {
- com.caucho.jsp.QPageContext pageContext = (com.caucho.jsp.QPageContext)
- com.caucho.jsp.QJspFactory.create().getPageContext(
- this, request, response, null, true, 8192, true);
- javax.servlet.jsp.JspWriter out = (javax.servlet.jsp.JspWriter)
- pageContext.getOut();
- javax.servlet.ServletConfig config = getServletConfig();
- javax.servlet.Servlet page = this;
- javax.servlet.http.HttpSession session = pageContext.getSession();
- javax.servlet.ServletContext application = pageContext.getServletContext();
- response.setContentType("text/html; charset=GB2312");
- try {
- pageContext.write(_jsp_string0, 0, _jsp_string0.length);
- out.print(("Hello World!!!!!!"));
- pageContext.write(_jsp_string1, 0, _jsp_string1.length);
- } catch (java.lang.Throwable e) {
- pageContext.handlePageException(e);
- } finally {
- JspFactory.getDefaultFactory().releasePageContext(pageContext);
- }
- }
-
- private com.caucho.java.LineMap _caucho_line_map;
- private java.util.ArrayList _caucho_depends = new java.util.ArrayList();
-
- public boolean _caucho_isModified()
- {
- if (com.caucho.util.CauchoSystem.getVersionId() != -1355223632)
- return true;
- for (int i = _caucho_depends.size() - 1; i >= 0; i--) {
- com.caucho.vfs.Depend depend;
- depend = (com.caucho.vfs.Depend) _caucho_depends.get(i);
- if (depend.isModified())
- return true;
- }
- return false;
- }
-
- public long _caucho_lastModified()
- {
- return 0;
- }
-
- public com.caucho.java.LineMap _caucho_getLineMap()
- {
- return _caucho_line_map;
- }
-
- public void init(com.caucho.java.LineMap lineMap,
- com.caucho.vfs.Path appDir)
- throws javax.servlet.ServletException
- {
- com.caucho.vfs.Path resinHome = com.caucho.util.CauchoSystem.getResinHome();
- com.caucho.vfs.MergePath mergePath = new com.caucho.vfs.MergePath();
- mergePath.addMergePath(appDir);
- mergePath.addMergePath(resinHome);
- mergePath.addClassPath(getClass().getClassLoader());
- _caucho_line_map = new com.caucho.java.LineMap
- ("_helloworld__jsp.java", "/helloworld.jsp");
- _caucho_line_map.add(1, 1);
- _caucho_line_map.add(1, 27);
- _caucho_line_map.add(7, 28);
- com.caucho.vfs.Depend depend;
- depend = new com.caucho.vfs.Depend(appDir.lookup("helloworld.jsp"),
- 1071120876000L, 155L);
- _caucho_depends.add(depend);
- }
-
- private static byte []_jsp_string1;
- private static byte []_jsp_string0;
- static {
- try {
- _jsp_string1 = "\r\n</BODY>\r\n</HTML>".getBytes("GB2312");
- _jsp_string0 = "\r\n<HTML>\r\n<HEAD>\r\n<TITLE>Hello World</TITLE>\r\n</HEAD>\r\n<BODY>\r\n".
- getBytes("GB2312");
- } catch (java.io.UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- }
從上面可以看出,HelloWorld.jsp在運行時首先解析成一個Java類_helloworld__jsp.java,該類繼承于com.caucho.jsp.JavaPage基類,此基類實現了HttpServlet接口。可見,JSP在運行前首先將編譯為一個Servlet,這就是理解JSP技術的關鍵。
我們還知道JSP頁面中內置了幾個對象,如pageContext、application、config、page、session、out等,你可能會奇怪,為什么在JSP中的代碼片斷中可以直接使用這些內置對象。觀察_jspService()方法,實際上這幾個內置對象就是在這里定義的。在對JSP文件中的代碼片斷進行解析之前,先對這幾個內置對象進行初始化。
原來jsp文件中的“<%="Hello World!!!!!!"%>”被翻譯成了“out.print(("Hello World!!!!!!"));”。
jsp引擎將HelloWorld.jsp解析成_helloworld__jsp.java后,再將_helloworld__jsp.java編譯成_helloworld__jsp.class,接著執行_helloworld__jsp.class,這時才通過out內置對象將HelloWorld.jsp頁面內容送至客戶端的瀏覽器。
最后,_helloworld__jsp.class就存入了服務器電腦的內存里,往后再執行請求頁面HelloWorld.jsp時,就直接執行存于服務器電腦內存里面的_helloworld__jsp.class了。
本篇“JSP技術”到此結束,一路走來,筆者詳述了jsp的整個框架結構以及開發中最核心的技術應用,最后還從全新的角度對jsp的運行原理進行了闡述。現在,你是否感覺自己已經是個jsp的高手了呢?
--------------------====全文完===-----------------
posted on 2005-10-24 22:36
zjw_albert 閱讀(277)
評論(0) 編輯 收藏