
4.6 内置对象
Servlet API规范包含了一些接口,这些接口向开发者提供了方便的抽象,这些抽象封装了对象的实现。如HttpServletRequest接口代表从客户发送的HTTP数据,其中包含头信息、表单参数等。
JSP根据Servlet API规范提供了相应的内置对象,开发者不用事先声明就可以使用标准的变量来访问这些对象。JSP共提供九种内置对象:request、reponse、out、session、application、config、pagecontext、page和exception。JSP编程时要求熟练应用这些内置对象。下面重点讲解编程中经常使用的一些内置对象。
4.6.1 request对象
request对象是JSP编程中最常用的对象,代表的是来自客户端的请求,它封装了用户提交的信息,例如,在FORM表单中填写的信息等。通过调用request对象相应的方法可以获取关于客户请求的信息。它实际上等同于Servlet的HttpServletRequest对象。常用的方法如表4-1所示。
表4-1 request对象和常用方法

下面通过向Web应用添加JSP页面jspFunction.jsp来演示request对象的方法调用。完整代码如程序4-24所示。
程序4-24:jspFunction.jsp
<%@ page contentType="text/html; charset=GB2312" %> <%@ page import="java.util.*" %> <HTML> <BODY > <Font size=5> <BR>客户使用的协议是: <%String protocol=request.getProtocol(); out.println(protocol); %> <BR>获取接受客户提交信息的页面: <%String path=request.getServletPath(); out.println(path); %> <BR>接受客户提交信息的长度: <% int length=request.getContentLength(); out.println(length); %> <BR>客户提交信息的方式: <%String method=request.getMethod(); out.println(method); %> <BR>获取HTTP头文件中User-Agent的值: <%String header1=request.getHeader("User-Agent"); out.println(header1); %> <BR>获取HTTP头文件中accept的值: <%String header2=request.getHeader("accept"); out.println(header2); %> <BR>获取HTTP头文件中Host的值: <%String header3=request.getHeader("Host"); out.println(header3); %> <BR>获取HTTP头文件中accept-encoding的值: <%String header4=request.getHeader("accept-encoding"); out.println(header4); %> <BR>获取客户的IP地址: <%String IP=request.getRemoteAddr(); out.println(IP); %> <BR>获取客户端的名称: <%String clientName=request.getRemoteHost(); out.println(clientName); %> <BR>获取服务器的名称: <%String serverName=request.getServerName(); out.println(serverName); %> <BR>获取服务器的端口号: <% int serverPort=request.getServerPort(); out.println(serverPort); %> <BR>获取客户端提交的所有参数的名字: <% Enumeration enum1=request.getParameterNames(); while(enum1.hasMoreElements()) {String s=(String)enum1.nextElement(); out.println(s); } %> <BR>获取头名字的一个枚举: <% Enumeration enum_headed=request.getHeaderNames(); while(enum_headed.hasMoreElements()) {String s=(String)enum_headed.nextElement(); out.println(s); } %> <BR>获取头文件中指定头名字的全部值的一个枚举: <% Enumeration enum_headedValues=request.getHeaders("cookie"); while(enum_headedValues.hasMoreElements()) {String s=(String)enum_headedValues.nextElement(); out.println(s); } %> <BR> </Font> </BODY> </HTML>
程序说明:程序通过调用request对象的各个方法来演示如何来获取与请求相关的各种属性信息,如客户端地址、协议、主机名称及HTTP文件头信息等。
保存程序并重新发布Web应用,打开IE浏览器,在地址栏输入http://localhost:8080/JspBasic/jspFunction.jsp,得到如图4-17所示的运行结果界面。

图4-17 利用request对象获取请求信息
利用表单的形式向服务器端提交信息是Web编程中最常用的方法。下面以一个调查问卷为例来演示如何通过request对象来获取表单提交的信息。首先生成提交问卷信息的静态页面input.html,完整代码如程序4-25所示。
程序4-25:input.html
<! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <meta charset="UTF-8"></head> <body> <form action="getParam.jsp"> 姓名<input type="text" name="UserName"> <br> 选出你喜欢吃的水果: <input type="checkbox" name="checkbox1" value="苹果"> 苹果 <input type="checkbox" name="checkbox1" value="西瓜"> 西瓜 <input type="checkbox" name="checkbox1" value="桃子"> 桃子 <input type="checkbox" name="checkbox1" value="葡萄"> 葡萄 <input type="submit" value="提交"> </form> </body></html>页面运行结果如图4-18所示。
下面添加获取客户端提交的问卷信息的页面getParam.jsp,完整代码如程序4-26所示。

图4-18 调查问卷页面
程序4-26:getParam.jsp
<%@ page contentType="text/html; charset=UTF-8" %> <HTML> <BODY> 你好, <%! String Name; %> <% request.setCharacterEncoding("UTF-8"); Name=request.getParameter("UserName"); String stars=new String("你喜欢吃的水果有:"); String[] paramValues = request.getParameterValues("checkbox1"); for(int i=0; i<paramValues.length; i++)stars+=paramValues[i]+" "; %> <%=Name%> <br> <%=stars%> </BODY> </HTML>
程序说明:JSP页面通过调用内置request对象的getParameter方法来获取被调查者的姓名,对于多值参数(如本例中的checkbox1)则调用getParameterValues方法来返回一个包含所有参数值的数组。
保存程序并重新发布Web应用,打开IE浏览器,在地址栏输入http://localhost:8080/JspBasic/input.html,得到如图4-18所示的运行结果页面。在“姓名”文本框输入姓名信息“张三”,选择相应的选项并提交后,将得到如图4-19所示的运行结果页面。可以看到输入信息已经成功获取。

图4-19 调查问卷运行结果页面
注意:使用request对象获取信息要格外小心,要避免使用空对象,否则会出现NullPointerException异常。
下面演示如何获取下拉列表框提交的信息。首先生成信息提交页面select.jsp。完整代码如程序4-27所示。
程序4-27:select.jsp
<HTML> <%@ page contentType="text/html; charset=GB2312" %> <BODY ><Font size=5 > <FORM action="sum.jsp" method=post name=form> <P>选择计算方式 <Select name="sum" size=2> <Option Selected value="1">计算1到n的连续和 <Option value="2">计算1到n的平方和 <Option value="3">计算1到n的立方和 </Select> <P>选择n的值: <Select name="n" > <Option value="10">n=10 <Option value="20">n=20 <Option value="30">n=30 <Option value="40">n=40 <Option value="50">n=50 <Option value="100">n=100 </Select> <BR><BR> <INPUT TYPE="submit" value="提交" name="submit"> </FORM> </FONT> </BODY> </HTML>
程序说明:程序以两个下拉列表框的形式向服务器端提交信息,运行结果如图4-20所示。

图4-20 通过Select下拉列表框提交信息
下面生成获取下拉列表框提交信息的页面sum.jsp。完整代码如程序4-28所示。
程序4-28:sum.jsp
<HTML> <%@ page contentType="text/html; charset=GB2312" %> <BODY ><Font size=5 > <% long sum=0; String s1=request.getParameter("sum"); String s2=request.getParameter("n"); if(s1= =null) {s1=""; } if(s2= =null) {s2="0"; } if(s1.equals("1")) {int n=Integer.parseInt(s2); for(int i=1; i<=n; i++) {sum=sum+i; } } else if(s1.equals("2")) {int n=Integer.parseInt(s2); for(int i=1; i<=n; i++) {sum=sum+i*i; } } else if(s1.equals("3")) {int n=Integer.parseInt(s2); for(int i=1; i<=n; i++) {sum=sum+i*i*i; } } %> <P>您的求和结果是<%=sum%> </FONT> </BODY> </HTML>
程序说明:程序通过getParameter(String name)方法来获取下拉列表框的值。在这里进行了意外处理。因为在客户不做任何选择的情况下调用getParameter(String name)方法获取的参数值为null。另外,由于通过getParameter(String name)只能获取String类型的值,因此要想获取整型参数n,必须通过调用静态方法Integer.parseInt(String name)进行类型转换。运行结果如图4-21所示。

图4-21 程序4-28运行结果页面
从上面的例子可以看出,通过参数的形式只能传递String类型的值,为了更加灵活地在客户与服务器间传递信息,还可以通过属性对象来传递数据,属性对象可以是任何类型的数据。首先生成向request对象中添加属性对象的页面Attribute_send.jsp,完整代码如程序4-29所示。
程序4-29:Attribute_send.jsp
<%@ page language="java" %> <%@ page import="java.util.ArrayList" %> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>Lomboz JSP</title> </head> <body bgcolor="#FFFFFF"> <% ArrayList ar= new ArrayList(); String he="hello"; ar.add(he); int m=3; ar.add(Integer.toString(m)); request.setAttribute("name", "peter"); request.setAttribute("value", ar); %> <h3>Attribue传递参数示例</h3> <JSP:forward page="Attribute_receive.jsp"> </JSP:forward> </body> </html>
程序说明:程序通过调用request对象的setAttribute(String name, java.lang.Object o)方法来给request对象添加两个属性对象:其中一个是String类型,另一个是ArrayList类型。然后利用JSP的forward动作将请求传递到下一个页面。
下面生成从request对象中获取属性对象的页面Attribute_receive.jsp,完整代码如程序4-30所示。
程序4-30:Attribute_receive.jsp
<%@page contentType="text/html; charset=GB2312"%> <%@ page import="java.util.ArrayList" %> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>Lomboz JSP</title> </head> <body bgcolor="#FFFFFF"> <% String name =(String)request.getAttribute("name"); java.util.ArrayList content=new ArrayList(); content =(java.util.ArrayList)request.getAttribute("value"); int m=0; String promt=""; if(content! =null){ String temp=(String)content.get(1); m=Integer.parseInt(temp); promt=(String)content.get(0); } for(int i=0; i<m; i++){ %> <%=promt+" "+name%> <br> <%}%> </body> </html>
程序说明:程序通过调用getAttribute(String name)方法来获取请求中提交的属性对象并将其显示在页面上,其中,name为属性对象的名称。由于对象是以Object类型返回的,因此在使用返回的属性对象前必须根据属性对象原来的类型进行强制类型转换。程序运行结果如图4-22所示。

图4-22 通过请求属性传递信息
注意:使用setAttribute方法向request对象添加的属性对象只能是可以序列化的对象类型数据,而不能是基本类型数据(如int、float等)。
4.6.2 response对象
response对象向客户端发送数据,如Cookie、HTTP文件头信息等。response对象代表的是服务器对客户端的响应,也就是说,可以通过response对象来组织发送到客户端的信息。但是由于组织方式比较底层,所以不建议一般程序开发人员使用,需要向客户端发送文字时直接使用out对象即可。
response对象常用的方法有:
(1)addCookie(Cookie cookie)。向response对象添加一个Cookie对象,用来保存客户端的用户信息。如下面代码:
<%cookie mycookie=new Cookie("name", "hyl"); response.addCookie(mycookie); %>
可以通过request对象的getcookies方法获得这个Cookie对象。
(2)addHeader(String name, String value)。添加HTTP文件头,该Header将会传到客户端,若同名的Header存在,则原来的Header会被覆盖。
(3)containsHeader(String name)。判断指定名字的HTTP文件头是否存在并返回布尔值。
(4)sendError(int sc)。向客户端发送错误信息,常见的错误信息包括505:服务器内部错误;404:网页找不到错误。如:
response.sendError(response.SC_NO_CONTENT);
(5)setHeader(String name, String value)。设定指定名字的HTTP文件头的值,若该值存在,则它会被新值覆盖。如,让网页每隔5秒刷新一次。
<% response.setHeader("Refresh", "5"); %>
(6)setContentType (String value)。用来设定返回response对象类型,如
<% response.setContentType("Application/pdf"); %>
(7)sendRedirect(String url)。将请求重新定位到一个新的页面。在4.3节曾学过如何利用<jsp: forward >将页面请求转到一个新的服务器资源上。这里必须了解<jsp: forward >动作组件与sendRedirect(String url)这两种服务器间重定位方式之间的区别。response. sendRedirect()其实是向浏览器发送一个特殊的Header,然后由浏览器来做转向,转到指定的页面,所以用sendRedirect()时,浏览器的地址栏中可以看到地址的变化;而<jsp:forward page="url"/>则不同,它是直接在服务器端执行重定位的,浏览器并不知道,这一点从浏览器的地址并不变化可以证实。
下面通过例子来演示如何利用response对象进行重定位。首先生成重定位前的页面greeting.jsp,完整代码如程序4-31所示。
程序4-31:greeting.jsp
<%@ page language="java" %> <%@ page import="java.util.*"%> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>Lomboz JSP</title> </head> <body bgcolor="#FFFFFF"> <% Date today =new Date(); int h= today.getHours(); if(h<12)response.sendRedirect("morning.jsp"); else response.sendRedirect("afternoon.jsp"); %> </body> </html>
下面生成重定向的两个页面morning.jsp和afternoon.jsp,代码分别如程序4-32和程序4-33所示。
程序4-32:morning.jsp
<%@ page contentType="text/html; charset=GB2312" %> <%@ page language="java" %> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>Lomboz JSP</title> </head> <body bgcolor="#FFFFFF"> 早上好! </body> </html>
程序4-33:afternoon.jsp
<%@ page contentType="text/html; charset=GB2312" %> <%@ page language="java" %> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>Lomboz JSP</title> </head> <body bgcolor="#FFFFFF"> 下午好! </body> </html>
保存程序并重新发布Web应用,打开IE浏览器,在地址栏输入http://localhost:8080/JspBasic/greeting.jsp,看看会得到什么运行结果。调整系统时间,然后重新请求http://localhost:8080/JspBasic/greeting.jsp,看看又会得到什么运行结果。
注意:仔细观察浏览器的地址栏,可以看到地址栏中的地址发生了变化,这说明页面的重定位是通过客户端重新发起请求实现的,这一点是与<jsp:forward>最本质的区别。
4.6.3 session对象
3.7节对于Servlet编程中的会话跟踪技术进行了深入研究。同样,JSP提供了内置对象session来支持Web应用程序开发过程中的会话管理。可以通过调用session对象的setAttribute和getAttribute方法来添加或者读取存储在会话中的属性值。
注意:session中保存和检索的信息不能是int等基本数据类型,而必须是Java Object对象。
下面通过一个防止重复登录的例子来演示如何利用Session进行Web应用开发。首先生成提交登录信息的静态页面login_session.html。完整代码如程序4-34所示。
程序4-34:login_session.html
<html> <body> <form action="logcheck.jsp"> 姓名<input type="text" name="UserName"> <input type="submit" value="提交"> </form> </body> </html>
页面运行时显示如图4-23所示。

图4-23 提交登录信息的静态页面
下面生成进行登录处理的JSP页面logcheck.jsp,完整代码如程序4-35所示。
程序4-35:logcheck.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@page import="java.util.*" %> <HTML> <BODY> <% request.setCharacterEncoding("UTF-8"); String promt=new String(); String Name=request.getParameter("UserName"); boolean hasLog=false; ArrayList names= (ArrayList)session.getAttribute("lognames"); if(names==null){ names=new ArrayList(); names.add(Name); session.setAttribute("lognames", names); promt=" 欢迎登录!你的名字已经写入session "; }else{ for(int i=0; i<names.size(); i++){ Stringtemp=(String)names.get(i); if(temp.equals(Name)){ promt="你已经登录"; hasLog=true; break; } } if(! hasLog){ names.add(Name); session.setAttribute("lognames", names); promt=" 欢迎登录!你的名字已经写入session "; } } %> <br> <%=promt%> </BODY> </HTML>
程序说明:程序首先调用request.getParameter方法获取提交的用户名称信息,然后调用session.getAttribute方法获取已登录人员名称列表,从列表中查找此用户是否已经登录。如果尚未登录,则将用户名称添加到一个存储已登录人员名称的列表ArrayList中,并通过调用session.setAttribute方法更新会话中保存的已登录人员名称列表信息;否则,提示用户已经登录。
保存程序并重新发布Web应用,打开IE浏览器,在地址栏输入http://localhost:8080/JspBasic/login_session.html,得到如图4-23所示的运行结果页面。在“姓名”文本框中输入“李四”,单击“提交”按钮提交页面信息,得到如图4-24所示运行结果页面,提示已将登录者的姓名写入了session。单击浏览器工具栏的“后退”按钮,在“姓名”文本框中重新输入“李四”,单击“提交”按钮提交页面信息,由于已将登录者的姓名写入了session,因此系统将提示用户已经登录,运行结果如图4-25所示。

图4-24 第一次登录成功运行结果页面

图4-25 重复登录运行结果页面
4.6.4 application对象
application对象代表运行在服务器上的Web应用程序,相当于Servlet上下文,一旦创建,除非服务器关闭,否则将一直保持下去。
application常用的方法如表4-2所示。
表4-2 application对象的常用方法

下面通过一个JSP页面来演示application对象的常用方法的使用。完整代码如程序4-36所示。
程序4-36:application.jsp
<%@ page contentType="text/html; charset=gb2312"%> <html> <head><title>application对象示例</title><head> <body> <% out.println("Java Servlet API Version "+application.getMajorVersion() +"."+application.getMinorVersion()+"<br>"); out.println("application.jsp's MIME type is:"+application.getMimeType("application.jsp") +"<br>"); out.println("URL of 'application.jsp' is: "+application.getResource("/application.jsp")+"<br>"); out.println("getServerInfo()="+application.getServerInfo()+"<br>"); out.println(application.getRealPath("application.jsp")); application.log("Add a Record to log_file"); %> </body> </html>
程序说明:通过调用application对象的各种方法来获取application对象的各种属性信息,其中getMajorVersion()用来获取服务器支持的Servlet版本,getMimeType()用来获取组件的MIME类型,getServerInfo()用来获取服务器版本信息等。程序运行结果如图4-26所示。

图4-26 利用application获取服务器信息
在application对象中也可以存储属性信息,由于application对象在整个Web应用的过程中都有效,因此在application对象中最适合放置整个应用共享的信息。但由于application对象生命周期长,因此对于存储在application对象中的属性对象要及时清理,以避免占用太多的服务器资源。下面以一个网页计数器的例子来说明如何在application对象中存储属性信息,网页计数器JSP页面代码如程序4-37所示。
程序4-37:counter.jsp
<%@ page contentType="text/html; charset=gb2312"%> <! DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> <html> <head> <title>网页计数器</title> </head> <body> <% if (application.getAttribute("counter")= =null) application.setAttribute("counter", "1"); else{ String times=null; times=application.getAttribute("counter").toString(); int icount=0; icount=Integer.valueOf(times).intValue(); icount++; application.setAttribute("counter", Integer.toString(icount)); } %> 您是第<%=application.getAttribute("counter")%>位访问者! </body> </html>
程序说明:为实现网页计数功能,向application对象中添加一个名为counter的属性。每次网页被访问时,通过调用application对象的getAttribute方法获取网页计数器的值,通过application对象的setAttribute方法将网页计数器更新后的值重新添加到application对象中。由于Application对象的生命周期是整个Web应用程序的生命周期,因此它可以准确地记录Web应用运行期间网页被访问的次数。
打开浏览器,在地址栏中输入http://localhost:8080/JspBasic/counter.jsp,可以得到如图4-27所示页面。不断刷新页面,可以看到网页计数不断更新。即使关掉浏览器,再重新打开运行,网页计数器仍然准确地记录着页面被访问的次数。这是因为存储在application对象中的网页计数变量在服务器运行期间一直存在。

图4-27 网页计数器
4.6.5 out对象
out对象代表了向客户端发送数据的对象,与response对象不同,通过out对象发送的内容将是浏览器需要显示的内容,是文本一级的,可以通过out对象直接向客户端写一个由程序动态生成HTML文件。常用的方法除了print和println之外,还包括clear、clearBuffer、flush、getBufferSize和getRemaining,这是因为out对象内部包含了一个缓冲区,所以需要一些对缓冲区进行操作的方法。
4.6.6 exception对象
exception对象用来处理JSP文件在执行时所有发生的错误和异常,有三个常用方法。
(1)getMessage():返回错误信息。
(2)printStackTrace():以标准错误的形式输出一个错误和错误的堆栈。
(3)toString():以字符串的形式返回一个对异常的描述。
注意:必须在<%@ page isErrorPage="true" %>的情况下才可以使用Exception对象。
下面通过一个示例来演示exception对象在处理JSP错误和异常情况下的应用。首先生成一个抛出意外的JSP页面makeError.jsp,完整代码如程序4-38所示。
程序4-38:makeError.jsp
<%@ page errorPage="exception.jsp" %> <HTML> <HEAD> <TITLE>错误页面</TITLE> </HEAD> <BODY> <% String s = null; s.getBytes(); //这将抛出NullPointException异常 %> </BODY> </HTML>
下面生成利用exception对象处理JSP错误和异常的页面exception.jsp,完整代码如程序4-39所示。
程序4-39:exception.jsp
<%@ page contentType="text/html; charset=gb2312"%> <%@ page isErrorPage="true" %> <html> <body bgcolor="#ffffc0"> <h1>错误信息显示</h1> <br>An error occured in the bean. Error Message is: <br> <%= exception.getMessage() %><br> <%= exception.toString()%><br> </body> </html>
程序说明:打开浏览器,在地址栏中输入http://localhost:8080/JspBasic/makeError.jsp,可以得到如图4-28所示的页面。程序makeError.jsp中将字符串s置为null,然后调用其getByte方法,势必抛出异常,由于通过语句<%@ page errorPage="exception.jsp" %>定义了此页面的错误处理页面为exception.jsp,因此错误信息导向页面exception.jsp。在exception.jsp中执行语句<%@ page isErrorPage="true" %>,因此可以调用内置对象exception的各种方法来显示错误信息。

图4-28 利用exception对象显示错误信息
4.6.7 内置对象的作用范围
任何一个Java对象都有其作用域范围,JSP的内置对象也不例外。归纳起来,共有四种范围:
(1)page。page范围内的对象仅在JSP页面范围内有效。超出JSP页面范围,则对象无法获取。
(2)request。客户向服务器发起的请求称为request(请求)。由于采用<jsp:forward>和response.sendRedirect()等重定位技术,客户端发起的request请求可以跨越若干个页面。因此定义为request范围的JSP内置对象可以在request范围内的若干个页面内有效。
(3)session。客户端与服务器的交互过程,称为session(会话)。在客户端与服务器的交互过程中,可以发起多次请求,一个session可以包含若干个request。定义为session范围的JSP内置对象可以跨越若干个request范围有效。
(4)application。部署在服务器上的Web应用程序与所有客户端的交互过程,称为application。一个application可以包含若干个session。定义为application范围的JSP内置对象可以在跨越若干个session的范围内有效。
综上所述,一个Web服务器上可以部署多个application,一个application可以包含多个session,一个session可以包含若干个request,一个request可以包含若干个page。以一个聊天室Web应用为例,作为部署在服务器上的一个application,每个加入到聊天室的客户与服务器间的交互过程即聊天过程为一个session。在客户A聊天过程中,它既可以向客户B发送信息,又可以向客户C发送信息,客户A向客户B和客户C发送信息的过程都是一个request。
JSP常见内置对象及其对应的Java类型和作用范围如表4-3所示。
表4-3 JSP内置对象对应类型及作用范围
