一个JSP页面不仅可以有HTML标记和JSP标记,而且可以有成员变量声明,JSP页面可以在Java程序片和Java表达式中使用JSP页面声明的成员变量。有些成员变量不用声明就可以在JSP页面的脚本(Java程序片和Java表达式)中使用,这就是所谓的内置对象。
内置对象有request、response、session、application、out。
一 request对象
为了更好地理解request对象,需要了解HTTP。HTTP是客户与服务器之间一种请求信息与相应信息的通信协议。我们使用浏览器从网站获取HTML页面或JSP页面时,遵守的就是HTTP。HTTP规定了信息在Internet上的传输方法,特备规定了浏览器与服务器的交互方法。
从网站获取页面时,浏览器在网站上打开了一个对网络服务器的连接,并发出请求。服务器收到请求并作出响应,响应结束后,客户浏览器和服务器之间的连接关闭。所以,HTTP被称为“请求和响应”协议。
用户通过在浏览器的地址栏中输入一个HTML或JSP页面所在的服务器的地址和页面的名字来请求该页面。当用户通过浏览器请求一个页面时,浏览器将请求发送给服务器。按照HTTP,浏览器发送的请求具有一定的结构,请求包括一个请求行、头域和表单提交的信息体等。服务器会根据浏览器发送来的请求作出响应,并将相应结果返回给用户的浏览器。最简单的请求是对页面的一个简单请求,例如:
GET/hello.html HTTP/1.1
Host:www.sina.com.cn
上面的第一行称为请求行,含有请求使用的方法、请求的资源和请求使用的协议;第二行称为头,给出请求的主机地址,其中请求的方法是get方法,请求的资源是hello.html,使用HTTP 1.1版本,请求的主机是www.sina.com.cn。
一个典型请求通常包含许多头,称为请求的HTTP头,提供了关于信息体的附加信息及请求的来源。其中有些头是标准的,有些头与特定的浏览器有关。
一个请求还可能包含信息体,如信息体可包含HTML表单的内容。在HTML表单上单击【submit】按钮时,如果表单使用ACTION=”POST”或ACTION=”GET”特征,请求将使用post方法或get方法将表单中输入的信息发送给服务器。
1. 获取客户提交的信息
当用户请求一个JSP页面时,JSP页面所在的Tomcat服务器将用户的请求封装在内置对象request中。那么,该对象调用相应的方法可以获取封装的信息,也就是说,使用该对象可以获取用户浏览器提交的请求信息,以便做出相应的响应。尽管request对象有许多可调用的方法,但是request对象常用的方法是getParameter(String s),该方法获取表单提交的信息。
内置对象request对象是实现了ServletRequest接口类的一个实例,可以在Tomcat服务器的webapps\tomcat-docs\servletapi目录中查找ServletRequest接口的方法。
【例 1】用户可以使用example4_1.jsp提供的表单再次请求example4_1.jsp页面,可以在表单提供的文本框中输入一个数字,并提交给example4_1.jsp页面,该页面通过内置对象获取用户提交的数字,然后让一个Tag文件负责计算该数字的平方,并将计算结果返回给用户。
Example4_1.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ taglib tagdir="/WEB-INF/tags" prefix="com"%>
<HTML><BODY bgcolor=cyan><FONT size=3>
<FORM action="" method=post name=form>
<Input type="text" name="number">
<Input type="submit" value="提交" name="submit">
</FORM>
<% String textContent=request.getParameter("number");
if(textContent==null){
out.println("请在文本框中输入数字,按提交按钮");
}
else{
%> <p>调用Computer.tag文件负责计算平方根
<com:Computer number="<%=textContent %>" />
<% }
%>
</FONT></BODY></HTML>
Computer.tag
<%@ attribute name="number" %>
<% try{
double n=Double.parseDouble(number);
if(n>=0){
double r=Math.sqrt(n);
out.print("<BR>"+n+"的平方根:");
out.print("<BR>"+r);
}
else
out.print("<BR>"+"请输入一个正数");
}
catch(NumberFormatException e){out.print("<BR>"+"请输入数字字符");}
%>
效果如图所示:
在运行例4-1时,用户先在浏览器的地址栏中输入example4_1.jsp页面地址“http://127.0.0.1:8080/chapter4/example4_1.jsp”,服务器做出响应,将表单发送给用户的浏览器,服务器的内置对象request调用getPeremeter()方法获取用户通过表单的文本框提交的信息。但是由于用户首次请求example4_1.jsp页面,还没有在表单中输入信息,没有单击表单中的【提交】按钮,所以request调用getPeremeter()方法获取信息为null。当用户看到表单之后,就可以在表单的文本框中输入信息,单击【提交】按钮,再次请求example4_1.jsp,那么服务器再次作出响应。这时,request调用getPeremeter()方法,就可以获取用户在表单的文本框中输入的信息。
2. 处理汉字信息
当用request对象获取请求中的汉字字符信息时,可能出现乱码问题,所以对含有汉字字符的信息必须进行特殊的处理方式,即将
<%@ page contentType=”text/html;charset=GB2312” %>
中出现的“charset”中的首写字母小写,内置对象将获取信息重新编码,即用ISO-8859-1进行编码,并把编码存到一个字节数组中去,如下所示:
String str=request.getParameter(“gile);
Byte b[]=str.getBytes(“ISO-8859-1”);
Str=new String(b);
【例 2】页面文件example4_2.jsp通过一个表单向页面showMessage2.jsp提交信息,避免汉字出现乱码。
Example4_2.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<HTML><BODY bgcolor=cyan>
<FORM action="showMessage1.jsp" method=post name=form>
<Input type="text" name="boy">
<Input type="submit" value="提交给showMessage1.jsp" name="submit">
</FORM>
<FORM action="showMessage2.jsp" method=post name=form>
<Input type="text" name="boy">
<Input type="submit" value="提交给showMessage2.jsp" name="submit">
</FORM>
</BODY></HTML>
showMessage2.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<P>获取文本框提交的信息:
<% String textContent=request.getParameter("boy");
byte b[]=textContent.getBytes("ISO-8859-1");
textContent=new String(b);
%>
<BR> <%=textContent%>
<P>获取按钮的名字:
<% String buttonName=request.getParameter("submit");
byte c[]=buttonName.getBytes("ISO-8859-1");
buttonName=new String(c);
%>
<BR> <%=buttonName%>
</BODY></HTML>
3. Request对象的常用方法
我们已经知道,当用户访问一个JSP页面时,会提交一个HTTP请求给Tomcat服务器,这个请求包括一个请求行、HTTP头和信息体。例如:
POST/E.jsp/HTTP.1.1
Host:localhost:8080
Accept-encoding:gzip,deflate
其中,首行叫做请求行,规定了向访问的页面请求提交信息的方式,如post、get等方法,以及请求的页面的文件名字和使用的通信协议。
第2、3行分别是两个头,host、accept-encoding称为头名字,localhost:8080和gzip、deflate分别是它们的值。这里,host的值是E.jsp的地址。上面的请求有两个头:host和accept-encoding。一个典型的请求通常包含许多头,有些头是标准的,有些头与特定的浏览器有关。
尽管服务器非常关心用户提交的HTTP请求中表单的信息,如例4-1和例4-2中使用request的getParameter()方法获取表单提交的有关信息,但实际上,使用request对象调用相关方法可以获取请求的许多细节信息。内置对象request常用方法如下。
getProtocol():获取请求使用的通信协议,如http/1.1等。
getServletPath():获取请求的JSP页面所在的目录。
getContentLength():获取HTTP请求的长度。
getMethod():获取表单提交信息的方式,如post或get。
getHeader(String s):获取请求中头的值。一般,s参数可取的头名有accept、referrer、accept-language、content-type、accept-encoding、user-agent、host、content-length、connection、cookie等。比如,s取值user-agent将获取客户的浏览器的版本号等信息。
getHeaderNames():获取头名字的一个枚举。
getHeaders(String s):获取头的全部值的一个枚举。
getRemoteAddr():获取客户的IP地址。
getRemoteHost():获取客户机的名称。
getServerName():获取服务器的名称。
getServerPort():获取服务器的端口号。
getParameterNames():获取表单提交的信息体部分中name参数值的一个枚举。
【例 3】使用request的一些常用方法。
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY bgcolor=cyan><Font size=3>
<FORM action="" method=post name=form>
<Input type="text" name="boy">
<Input TYPE="submit" value="enter" name="submit">
<TABLE border=1>
<% String protocol=request.getProtocol();
String path=request.getServletPath();
String method=request.getMethod();
String header=request.getHeader("accept");
%>
<TR>
<TD>客户使用的协议是:</TD>
<TD>"<%=protocol %>"</TD>
</TR>
<TR>
<TD>用户请求的页面所在位置:</TD>
<TD>"<%=path %>"</TD>
</TR>
<TR>
<TD>用户提交信息的方式:</TD>
<TD>"<%= method %>"</TD>
</TR>
<TR>
<TD>获取HTTP头文件中accept的值:</TD>
<TD>"<%= header %>"</TD>
</TR>
</TABLE>
<BR>获取客户端提交的所有参数的名字:
<% Enumeration enumName=request.getParameterNames();
while(enumName.hasMoreElements()){
String s=(String)enumName.nextElement();
out.println(s);
}
%>
<BR>获取头名字的一个枚举:
<% Enumeration enumHeaded=request.getHeaderNames();
while(enumHeaded.hasMoreElements()){
String s=(String)enumHeaded.nextElement();
out.println(s);
}
%>
<BR>获取头文件中指定头名字的全部值的一个枚举:
<% Enumeration enumHeadedValues=request.getHeaders("cookie");
while(enumHeadedValues.hasMoreElements()){
String s=(String)enumHeadedValues.nextElement();
out.println(s);
}
%>
<P>文本框text提交的信息:
<% String textContent=request.getParameter("boy");
byte b[]=textContent.getBytes("ISO-8859-1");
textContent=new String(b);
if(textContent==null){
textContent="";
}
int length=textContent.length();
out.println(textContent);
out.println("文本框中字符的个数:"+length);
%>
</FONT></BODY></HTML>
二 response对象
当客户访问一个服务器的页面时,会提交一个HTTP请求,服务器收到请求后,返回HTTP响应。响应与请求类似,也有某种结构,每个响应都由状态行开始,可以包含几个头和可能的信息体。
Resquest对象获取客户请求提交的信息,与request对象相对应的对象是response内置对象。Response对象对客户的请求作出相应,向客户端发送数据。
1. 改变contentType属性的值
当一个客户请求访问一个JSP页面时,如果该页面用page指令设置页面的contentType属性的值为text/html,那么response对象按照这种属性值做出响应,将页面的静态部分返回给客户。由于page指令只能为contentType指定一个值,来决定相应的MIME类型,如果想动态地改变这个属性的值来响应客户,就需要让response对象调用setContentType(String s)方法来改变contentType的属性值:
Public void setContentType(String s)
该方法设置响应的MIME类型,参数s可取值text/html、text/plain、image/gif、image/jpeg、image/x-xbitmap、image/pjpeg、application/x-shockwave-flash、application/vnd.ms-powerpoint、application/vnd.ms-excel、application/msword等。
当服务器用setContentType方法动态改变了contentType的属性值,即响应的MIME类型,并将JSP页面的输出结果按照新的MIME类型返回给客户时,客户端要保证支持这种新的MIME类型。
【例4】客户单击不同的按钮,可以改变页面响应的MIME类型。当选择将当前页面用“MS-Word显示”时,JSP页面动态地改变contentType的属性值为application/msword,客户浏览器会调用本地的Word软件来显示当前页面;选择将当前页面用“MS-Powerpoint显示”时,JSP页面动态地改变contentType的属性值为application/vnd.ms-powerpoint,客户浏览器会调用本地的PowerPoint软件来显示当前页面。
Example4_4.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<HTML><BODY bgcolor=cyan><FONT size=2>
<P>在学习response对象的setContentType方法
<FORM action="" method="post" name=form>
<P>将当前页面用MS-Word显示吗?
<Input type="submit" value="MS-Word显示" name="submit">
<P>将当前页面用MS-Powerpoint显示吗?
<Input type="submit" value="MS-Powerpoint显示" name="submit">
</FORM>
<% String str=request.getParameter("submit");
if(str==null){
str="";
}
if(str.equals("MS-Word显示")){
response.setContentType("application/msword");
}
else if(str.equals("MS-Powerpoint显示")){
response.setContentType("application/vnd.ms-powerpoint");
}
%>
</FONT></BODY></HTML>
2. 设置相应的HTTP头
我们已经知道,当客户访问一个页面时,会提交一个HTTP头给服务器。同样,响应也包括一些头。Response对象可以使用如下方法:
addHeader(String head,String value);
setHeader(String head,String value);
动态添加新的响应头和头的值,将这些头发送给客户机的浏览器。如果添加的头已经存在,则先前的头被覆盖。
【例 5】为response对象添加一个响应头“refresh”,其值是“5”。那么,客户机收到这个头之后,5秒后将再次刷新页面example4_5.jsp,导致该页面每5秒刷新一次。
Example4_5.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY bgcolor=cyan><FONT size=4>
<P>现在的时间是:<BR>
<% out.println(""+new Date());
response.setHeader("Refresh","5");
%>
</FONT></BODY></HTML>
3. 重定向
在某些情况下,当响应客户机时,需要将客户机重新引导至另一个页面。例如,如果用户输入的表单信息不完整,就会再被引导到该表单的输入页面。
可以使用response的sendRedirect(URL url)方法实现客户的重定向。
【例 6】客户机在页面example4_6.jsp中填写表单提交给页面form.jsp,如果填写的表单不完整,就会重新定向到页面example4_6.jsp。
Example4_6.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<P>填写姓名:<BR>
<FORM action="form.jsp" method="post" name=form>
<Input type="text" name="boy">
<Input type="submit" value="Enter">
</FORM>
</BODY></HTML>
Form.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<HTML><BODY>
<% String str=null;
str=request.getParameter("boy");
byte b[]=str.getBytes("ISO-8859-1");
str=new String(b);
if(str.length()==0){
response.sendRedirect("example4_6.jsp");
}
else{
out.print("欢迎您来到本网页!");
out.print(str);
}
%>
</BODY></HTML>
4. 状态行
当服务器对客户请求进行响应时,首先要发送的是状态行,然后发送HTTP头和信息体。也就是说,状态行是响应的首行。
状态行包括3位数字的状态码和对状态代码的描述。下面列出了5种状态码的大概描述。
1yy:主要是实验性质的。
2yy:表明请求成功,如状态码200可以表明已成功取得了请求的页面。
3yy:表明在请求满足之前应采取进一步的行动。
4yy:当浏览器无法满足请求时,返回该状态码,如404表示请求的页面不存在。
5yy:表示服务器出现问题,如500说明服务器内部发生错误。
一般不需要修改状态行,在出现问题时,response对象会自动响应,发送相应的状态代码。我么也可以使用response对象的setStatus(int n)方法来设置状态行,如果设置的状态行已经存在,则先前的状态行被覆盖。如下是状态码表。
状态码 |
代码声明 |
状态码 |
代码声明 |
100 |
客户可以继续 |
404 |
请求的资源不可用 |
101 |
服务器正在升级协议 |
405 |
请求所用的方法时不允许的 |
201 |
请求成功且在服务器上创建了新的资源 |
406 |
请求的资源只能用请求不能接受的内容特性来响应 |
202 |
请求已经被接受但还没有处理完毕 |
407 |
客户必须得到认证 |
200 |
请求成功 |
408 |
请求超时 |
203 |
客户端给出的元信息不是发送服务器的 |
409 |
发生冲突,请求不能完成 |
204 |
请求成功,但没有新信息 |
410 |
请求的资源已经不可用 |
205 |
客户必须重置文档视图 |
411 |
请求需要一个定义的内容长度才能处理 |
206 |
服务器执行不了部分get请求 |
413 |
请求太大,被拒绝 |
300 |
请求资源有多种表示法 |
414 |
请求的URL太大 |
301 |
资源已经被永久移动到了新位置 |
415 |
请求的格式被拒绝 |
302 |
资源已经被临时移动到了新位置 |
500 |
服务器发生内部错误,不能服务 |
303 |
应答可以再另外一个URL钟找到 |
501 |
不支持请求的部分功能 |
304 |
GET方式请求不可用 |
502 |
从代理和网关接受了不合法的字符 |
305 |
请求必须通过代理来访问 |
503 |
HTTP服务暂时不可用 |
400 |
请求有语法错误 |
504 |
服务器在等待代理服务器应答时发生超时 |
401 |
请求需要HTTP认证 |
505 |
不支持请求的HTTP版本 |
403 |
取得了请求但拒绝服务 |
|
|
Example4_7.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY bgcolor=cyan><FONT size=2>
<P>点击下面的超链接:<BR>
<A href="welcome1.jsp"> welcome1.jsp欢迎你吗?
<BR> <A href="welcome2.jsp"> welcome2.jsp欢迎你吗?
<BR> <A href="welcome3.jsp"> welcome3.jsp欢迎你吗?
</FONT></BODY></HTML>
Welcome1.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<% response.setStatus(408);
out.print("能看到本页面吗?");
%>
</BODY></HTML>
Welcome2.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<% response.setStatus(200);
out.println("这是welcome2,能看到welcome2.jsp页面吗?");
%>
</BODY></HTML>
Welcome3.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<% response.setStatus(500);
%>
</BODY></HTML>
效果如图所示:
三 session对象
网络通信是以协议为基础的。HTTP是一种无状态协议,即客户机向服务器发出请求,然后服务器返回响应,连接就被关闭了,在服务器端不保留链接的有关信息。因此当下一次连接时,服务器已没有以前的连接信息了,无法判断这一次连接和以前的连接是否属于同一客户机。而且,当一个客户机访问一个服务器时,可能会在某个Web服务目录或其子目录中的几个页面反复连接,反复刷新一个页面,或不断地向一个页面提交信息等,服务器应当通过某种办法知道这是同一个客户机。也就是说,服务器必须使用某种手段记录有关连接的信息,这就需要Tomcat服务器提供的内置session(会话)对象。Session对象在客户和服务器进行信息交互中起着非常重要的作用。
内置对象session由Tomcat服务器负责创建,session是实现了HttpSession接口类的一个实例,可以在Tomcat服务器的webapps\tomcat-docs\servletapi中查找HttpSession接口的方法。
1. Session对象的ID
当一个客户首次访问Web服务目录中的一个JSP页面时,Tomcat服务器产生一个session对象。这个session对象调用相应的方法可以存储客户在访问该Web服务目录中各页面期间提交的各种信息,如姓名、号码等信息。这个session对象被分配了一个String类型的ID,Tomcat服务器同时将这个ID发送到客户机,存放在客户机的Cookie中。这样,session对象和客户机之间就建立起一一对应的关系,即每个客户机都对应着一个session对象,不同用户的session对象互不相同,具有不同的ID。当客户机在访问连接该服务目录的其他页面时,或从该服务目录里连接到其他服务器再回到该服务目录时,Tomcat服务器不再分配给客户机的新session对象,而是使用完全相同的一个,直到客户机关闭浏览器或这个session对象达到了最大生存时间,服务器取消客户机的session对象,即与客户机的会话对应关系消失。当客户机重新打开浏览器再连接到该服务目录时,服务器为该客户机再创建一个新的session对象。
注:同一个客户机在不同的服务目录中的session是互不相同的。
【例 8】客户机在服务器的某个Web服务目录中有3个页面:first.jsp、second和third.jsp之间进行连接,只要不关闭浏览器,3个页面的session对象是完全相同的。其中,first.jsp存放在Web服务目录chapter4中,second.jsp存放在目录chapter4\tom中,third.jsp存放在目录chapter4\jerry中。客户机先访问first.jsp页面,从这个页面再链接到second.jsp页面,然后从second.jsp再链接到third.jsp页面。
First.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY>
<P><% String id=session.getId();
out.println("您的session对象的ID是:<br>"+id);
String str=response.encodeRedirectURL("tom/second.jsp");
%>
<P>输入你的姓名连接到second.jsp
<FORM action="<%=str%>" method=post name=form>
<Input type="text" name="boy">
<Input type="submit" value="送出" name=submit>
</FORM>
</BODY>
</HTML>
Second.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<HTML><BODY><P>我是second.jsp页面
<% String id=session.getId();
out.println("您的session对象的ID是:<br>"+id);
String str=response.encodeRedirectURL("/chapter4/jerry/third.jsp");
%>
<P>点击超链接,连接到third.jsp的页面。
<A href="<%=str%>">欢迎去third.jsp页面!</A>
</BODY></HTML>
Third.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY><P>我是third.jsp页面
<% String id=session.getId();
out.println("您的session对象的ID是:<br>"+id);
String str=response.encodeRedirectURL("/chapter4/first.jsp");
%>
<P>点击超链接,连接到first.jsp的页面。
<A href="<%=str%>">欢迎去first.jsp!</A>
</BODY></HTML>
效果如图所示:
2. Session对象存储数据
Session对象驻留在服务器端,该对象调用某些方法保存客户机在访问某个Web服务目录期间的有关数据。Session对象使用下列方法处理数据。
(1)public void setAttribute(String key,Object obj)
Session对象可以调用该方法将参数Object指定的对象obj添加到session对象中,并为添加的对象指定了一个索引关键字,如果添加的两个对象的关键字相同,则先前添加的对象被清除。
(2)public Object getAttribute(String key)
获取session对象含有的关键字是key的对象。由于任何对象都可以添加到session对象中,因此用该方法取回对象时,应强制转化为原来的类型。
(3)public Enumeration getAttributeNames()
Session对象调用该方法产生一个枚举对象,该枚举对象使用nextElements()遍历session中的各对象所对应的关键字。
(4)public void removeAttribute(String name)
Session对象调用该方法移掉关键字key对应的对象。
当session对象处理数据时,非常像一个购物车。一个用户访问一个商场期间,分配给该用户的购物车可以放入和移出商品,用户可能在这个商场许多柜台购物,当用户离开商场时,购物车取消。
【例 9】用session对象模拟购物车、存储客户的姓名和购买的信息,3个JSP页面都保存在Web服务目录chapter4中。
Main.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<HTML><BODY bgcolor=yellow><FONT size=2>
<P>欢迎来到本页面,请输入您的姓名
<FORM action="" method=post name=form>
<Input type="text" name="name">
<Input type="submit" value="送出" name=submit>
</FORM>
<% String name=request.getParameter("name");
if(name==null){
name="";
}
else{
session.setAttribute("customerName",name);
}
%>
<% if(name.length()>0){
%> <P>点击超链接,连接到food.jsp的页面,去采购食品。
<A href="food.jsp"> 欢迎去食品柜台!</A>
<% }
%>
<FONT></BODY></HTML>
Food.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY bgcolor=cyan><FONT size=3>
<P>点击超链接,连接到main.jsp的页面,去修改姓名。
<A href="main.jsp"> 欢迎去main.jsp!</A>
<P>这里是食品柜台,请选择您要购买的食品:
<FORM action="" method=post name=form>
<Input type="checkbox" name="choice" value="香肠">香肠
<Input type="checkbox" name="choice" value="苹果">苹果
<Input type="checkbox" name="choice" value="酱油">酱油
<Input type="checkbox" name="choice" value="饮料">饮料
</BR>
<Input type="submit" value="提交" name="submit">
</FORM>
</FONT>
<% String foodName[]=request.getParameterValues("choice");
if(foodName!=null){
for(int k=0;k<foodName.length;k++){
session.setAttribute(foodName[k],foodName[k]);
}
}
%>
<P>点击超链接,连接到count.jsp的页面,去查看购物车中的商品。
<A href="count.jsp"> 欢迎去count.jsp!</A>
</BODY></HTML>
Count.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><P>这里是结账处,您的姓名以及选择的商品是:
<% String personName=(String)session.getAttribute("customerName");
byte b[]=personName.getBytes("ISO-8859-1");
personName=new String(b);
out.println("<br>您的姓名:"+personName);
Enumeration enumGoods=session.getAttributeNames();
out.println("<br>购物车中的商品:<br>");
while(enumGoods.hasMoreElements()){
String key=(String)enumGoods.nextElement();
String goods=(String)session.getAttribute(key);
byte g[]=goods.getBytes("ISO-8859-1");
goods=new String(g);
if(!(goods.equals(personName)))
out.println(goods+"<br>");
}
%>
<P>点击超链接,连接到food.jsp的页面,去购买食品。
<A href="food.jsp"> 欢迎去food.jsp!</A>
<P>点击超链接,连接到main.jsp的页面,去修改姓名。
<A href="main.jsp"> 欢迎去main.jsp!</A>
</FONT></BODY></HTML>
效果如图所示:
3. Session对象的生存期限
用户在某个Web目录的session对象的生存期限依赖于是否关闭浏览器,依赖于session对象是否调用invalidate()方法,使得session无效或session对象达到了设置的最长的“发呆”状态时间。如果关闭浏览器,那么用户的session消失。如果用户长时间不关闭浏览器,用户的session也可能消失,这是因为Tomcat服务器允许用户最长的“发呆”状态时间为30分钟。所谓“发呆”状态时间,是指用户对某个的Web服务目录发出的两次请求之间的间隔时间。比如,用户对某个Web服务目录下的JSP页面发出请求,并得到相应,如果用户不再对该Web服务目录发出请求,那么用户对该Web服务目录进入“发呆”状态,直到用户再次请求该Web服务目录时,“发呆”状态结束。
可以修改Tomcat服务器下的web.xml,重新设置各Web服务目录下的session对象的最长“发呆”时间。打开Tomcat安装目录中conf文件下的配置文件web.xml,找到
<session-config>
<session-timeout>30</session-timeout>
</session-config>
将其中的“30”修改成所要求的值即可。
Session对象可以使用下列方法获取或设置生存时间有关的信息。
Public long getCreationTime():可以获取该对象创建的时间,单位是毫秒。
Public long getLastAccessedTime():获取当前session对象最后一次被操作的时间,单位是毫秒。
Public int getMaxInactiveInterval():获取session对象最长的“发呆”时间,单位是秒。
Public void setMaxInactiveInterval(int interval):设置session对象最长的“发呆”时间,单位是秒。
Public boolean isNew():判断当前session是否是一个新建的会话。
Invalidate():使session无效。
【例 10】session对象使用setMaxInactiveInterval(int interval)方法设置最长的“发呆”状态时间为20秒。用户可以通过刷新页面检查是否达到了最长的“发呆”时间,如果两次刷新之间的间隔超过20秒,先前的session将被取消,将获得一个新的session对象。
Example4_10.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY bgcolor=yellow><FONT size=3>
<% session.setMaxInactiveInterval(20);
boolean boo=session.isNew();
out.println("<br>如果你第一次访问当前Web服务目录,您的会话是新的");
out.println("<br>如果你不是首次访问当前Web服务目录,您的会话不是新的");
out.println("<br>会话是新的吗?:"+boo);
out.println("<br>欢迎来到本页面,您的session允许的最大发呆时间为"+session.getMaxInactiveInterval()+"秒");
out.println("<br>您的session的Id是"+session.getId());
Long lastTime=(Long)session.getAttribute("lastTime");
if(lastTime==null){
long n=session.getLastAccessedTime();
session.setAttribute("lastTime",new Long(n));
}
else{
long m=session.getLastAccessedTime();
long n=((Long)session.getAttribute("lastTime")).longValue();
out.println("<br>您的发呆时间大约是"+(m-n)+"毫秒,大约"+(m-n)/1000+"秒");
session.setAttribute("lastTime",new Long(m));
}
%>
<FONT></BODY></HTML>
4. Session对象与URL重写
Session对象能与客户及建立起一一对应关系依赖于用户的浏览器是否支持Cookie。如果客户既不支持Cookie,那么不同网页之间的session对象可能是互不相同的,因为服务器无法将ID存放到客户机,就不能建立session对象与客户机的一一对应关系。我们将浏览器的Cookie设置为禁止后,再运行前面的例8,会得到不同的结果。即“同一客户机”对应了多个session对象,这样服务器就无法知道在这个页面上访问的实际上是同一个客户机。
如果客户机的浏览器不支持Cookie,可以通过URL重写来实现session对象的唯一性。所谓URL重写,就是当客户机从一个页面重新链接到一个页面时,通过向这个新的URL添加参数,把session对象的ID传带过去,这样就可以保障客户机在该网站各页面中的session对象是完全相同的。可以使用response对象调用encodeURL()或encodeRedirectURL()方法实现URL重写。比如,如果从tom.jsp页面连接到jerry页面,首先实现URL重写:
String str=response.enccodeRedirectURl(“jerry.jsp”);
然后将连接目标写成“<%=str%>”。
5. 计数器
计数器可以记录某个Web服务目录被不同用户的浏览器访问次数,但需要限制客户通过不断刷新页面或再次访问其他的页面来增加计数器的计数。当一个用户请求该Web服务目录下的任何一个JSP页面时首先检查用户的session对象中是否已经有计数,如果没有计数,立刻将当前的计数增1,并存到客户的session中。
【例 11】Web服务目录有2个JSP页面helloOne.jsp、helloTwo.jsp和1个tag文件count.tag。Count.tag文件负责计数。helloOne.jsp、helloTwo.jsp使用count.tag实现计数。用户首次请求helloOne.jsp和helloTwo.jsp的任何一个,都会使得网站的计数增1.
helloOne.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ taglib prefix="person" tagdir="/WEB-INF/tags" %>
<HTML><BODY>
<P>Welcome欢迎您访问本站
<person:count/>
<A href="helloTwo.jsp">欢迎去helloTwo.jsp参观</A>
</BODY></HTML>
helloTwo.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ taglib prefix="person" tagdir="/WEB-INF/tags" %>
<HTML><BODY>
<P>Welcome欢迎您访问本站
<person:count/>
<A href="helloTwo.jsp">欢迎去helloOne.jsp参观</A>
</BODY>
</HTML>
Count.tag
<%@ tag import="java.io.*" %>
<FONT size=4>
<%! int number=0;
File file=new File("count.txt");
synchronized void countPeople(){
if(!file.exists()){
number++;
try{
file.createNewFile();
FileOutputStream out=new FileOutputStream("count.txt");
DataOutputStream dataOut=new DataOutputStream(out);
dataOut.writeInt(number);
out.close();
dataOut.close();
}
catch(IOException ee){}
}
else{
try{
FileInputStream in=new FileInputStream("count.txt");
DataInputStream dataIn=new DataInputStream(in);
number=dataIn.readInt();
number++;
in.close();
dataIn.close();
FileOutputStream out=new FileOutputStream("count.txt");
DataOutputStream dataOut=new DataOutputStream(out);
dataOut.writeInt(number);
out.close();
dataOut.close();
}
catch(IOException ee){}
}
}
%>
<% String str=(String)session.getAttribute("count");
if(str==null){
countPeople();
String personCount=String.valueOf(number);
session.setAttribute("count",personCount);
}
%>
<P><P>您是第
<%=(String)session.getAttribute("count")%>
个访问本网站的客户。
</FONT>
4. Out对象
Out对象是一个输出流,指向客户的浏览器的缓存区,out对象调用相应的方法可以将数据发送到客户端浏览器的缓存中。内置对象out对象是JspWriterout类的一个实例,可以在Tomcat服务器的webapps\tomcat-docs\servletapi中查找JspWriterout类的方法。
Out对象可调用如下方法用于各种数据的输出,例如:
Out.print(Boolean),out.println(boolean) 用于输出一个布尔值
Out.print(char),out.println(char) 输出一个字符
Out.print(double),out.println(double) 输出一个双精度的浮点数
Out.print(float),out.println(float) 用于输出一个单精度的浮点数
Out.print(long),out.println(long) 输出一个字符串对象的内容
Out.flush() 输出缓冲区里的内容
Out.close() 关闭流
方法println()和print()的区别是:println()会向缓存区写入一个换行,而print()不写入换行。但是浏览器的显示区域目前不识别println()写入的换行,如果希望浏览器显示换行,应当向浏览器写入<br>实现换行。
【例 12】使用out对象向客户输出包括表格等内容的信息。
Example4_12.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY>
<% int a=2200;long b=3456;boolean c=true;
out.println(a);
out.println(b);
out.print("<br>");
out.println(c);
%>
<Left> <P><FONT size=2>以下是一个表格</FONT>
<% out.print("<Font face=隶书 size=2>");
out.println("<table Border=1>");
out.println("<tr>");
out.println("<th width=80>"+"姓名"+"</th>");
out.println("<th width=60>"+"性别"+"</th>");
out.println("<th width=200>"+"出生日期"+"</th>");
out.println("</tr>");
out.println("<tr>");
out.println("<td>"+"张三"+"</td>");
out.println("<td>"+"男"+"</td>");
out.println("<td>"+"1998年5月"+"</td>");
out.println("</tr>");
out.println("<tr>");
out.println("<td>"+"李四"+"</td>");
out.println("<td>"+"男"+"</td>");
out.println("<td>"+"1987年10月"+"</td>");
out.println("</tr>");
out.println("</Table>");
out.print("</Font>");
%>
</Center></BODY></HTML>
五 Application对象
我们已经知道,不同用户的session对象互不相同。但有时候,用户之间可能需要共享一个对象,Tomcat服务器启动后,就产生了这样一个对象,它就是内置对象application。任何用户在所访问的服务目录的各页面时,这个application对象都是同一个,直到服务器关闭,这个application对象才被取消。
1. Application对象的常用方法
(1)public void setAttribute(String key,Object obj)
Application对象可以调用该方法将参数Object指定的对象obj添加到application对象中,并为添加的对象指定了一个索引关键字,如果添加的两个对象的关键字相同,则先前添加对象被清除。
(2)public Object getAttribute(String key)
获取application对象含有的关键字是key的对象。由于任何对象都可以添加到application对象中,因此用该方法取回对象时,应强制转化为原来的类型。
(3)public Enumeration getAttributeNames()
Application对象调用该方法产生一个枚举对象,该枚举对象使用nextElements()遍历application中的各对象所对应的关键字。
(4)public void removeAttribute(String key)
从当前application对象中删除关键字是key的对象。
(5)public String getServletInfo()
获取Servlet编译器的当前版本的信息。
由于application对象对所有的客户都是相同的,任何客户机对该对象中存储的数据的改变都会影响到其他客户机,因此在某些情况下,对该对象的操作需要实现同步处理。
注:有些服务器不直接支持使用application对象,必须用ServletContext类声明这个对象,再使用getServletContext()方法对这个application对象进行初始化。
2. 用application制作留言板
在下面的例13中,客户机通过submit.jsp向messagePane.jsp页面提交姓名、留言标题和留言内容,messagePane.jsp页面获取这些内容后,用同步方法将这些内容添加到一个向量中,然后将这个向量添加到application对象中。当用户查看留言板时,showMessage.jsp负责显示所有用户的留言内容,即从application对象中取出向量,然后遍历向量中存储的信息。在这里使用了向量这种数据结构,Java的java.util包中的Vector类负责创建一个向量对象。如果你已经学会使用数组,那么很容易就会使用向量。创建一个向量时不用像数组那样必须要给出数组的大小。向量创建后,如“Vector a=new Vector()”,a可以使用add(Object o)方法把任何对象添加到向量的末尾,向量的大小会自动增加。可以使用add(int index,Object o)方法把一个对象追加到该向量的指定位置。向量a可以使用elementAt(int index)方法获取指定索引处的向量的元素;a可以使用size()方法获取向量所含有的元素的个数。另外,与数组不同的是向量的元素类型不要求一致。需要注意的是,虽然可以把任何一种Java的对象放入一个向量,但是当从向量中取出一个元素时,必须使用强制类型转化运算符将其转化为原来的类型。
【例 13】
Submit.jsp
<%@ page contentType="text/html;charset=GB2312"%>
<HTML><BODY>
<FORM action="messagePane.jsp" method="post" name="form">
<P>输入您的名字:
<Input type="text" name="peopleName">
<BR>输入您的留言标题:
<Input type="text" name="Title">
<BR>输入您的留言:
<BR> <TextArea name="messages" ROWs="10" COLs="36" WRAP="physical">
</TextArea>
<BR><Input type="submit" value="提交信息" name="submit">
</FORM>
<FORM action="showMessage.jsp" method="post" name="form1">
<Input type="submit" value="查看留言板" name="look">
</FORM>
</BODY></HTML>
messagePane.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY>
<%! Vector v=new Vector();
ServletContext application;
synchronized void sendMessage(String s){
application=getServletContext();
v.add(s);
application.setAttribute("Mess",v);
}
%>
<% String name=request.getParameter("peopleName");
String title=request.getParameter("Title");
String messages=request.getParameter("messages");
if(name==null){
name="guest"+(int)(Math.random()*10000);
}
if(title==null){
title="无标题";
}
if(messages==null){
messages="无信息";
}
String time=new Date().toString();
String s="#"+name+"#"+title+"#"+time+"#"+messages+"#";
sendMessage(s);
out.print("您的信息已经提交!");
%>
<A href="submit.jsp">返回
<A href="showMessage.jsp">查看留言板
</BODY></HTML>
showMessage.jsp
<%@ page contentType="text/html;Charset=GB2312" %>
<%@ page import="java.util.*" %>
<HTML><BODY>
<% Vector v=(Vector)application.getAttribute("Mess");
out.print("<table border=2>");
out.print("<tr>");
out.print("<td bagcolor=cyan>"+"留言者姓名"+"</td>");
out.print("<td bagcolor=cyan>"+"留言标题"+"</td>");
out.print("<td bagcolor=cyan>"+"留言时间"+"</td>");
out.print("<td bagcolor=cyan>"+"留言内容"+"</td>");
out.print("</tr>");
for(int i=0;i<v.size();i++){
out.print("<tr>");
String message=(String)v.elementAt(i);
StringTokenizer fenxi=new StringTokenizer(message,"#");
out.print("<tr>");
int number=fenxi.countTokens();
for(int k=0;k<number;k++){
String str=fenxi.nextToken();
byte b[]=str.getBytes("ISO-8859-1");
str=new String(b);
if(k<number-1){
out.print("<td bgcolor=cyan>"+str+"</td>");
}
else{
out.println("<td><TextArea rows=3 cols=12>"+str+"</TextArea></td>");
}
}
out.print("</tr>");
}
out.print("</table>");
%>
</BODY></HTML>
效果如图所示: