文章目录
-------------------------------------------------
一.指令元素
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()%>
² 一个页面错误处理的例子
产生错误的页面文件为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);
- }
- %>
² 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.属性
- }
- }
² 一个简单的使用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对象的。具体的使用方法,我们还是通过例子来说明吧!!!
² 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 阅读(272)
评论(0) 编辑 收藏