第3章〓JSP内置对象 学习目的与要求 本章主要介绍request、response、out、session、application、pageContext、page、config和exception等内置对象。通过本章的学习,要求读者理解JSP内置对象的含义,掌握JSP内置对象的使用方法。 本章主要内容  request对象  response对象  out对象  session对象  application对象  pageContext对象  page对象  config对象  exception对象 有些对象在JSP页面中不需要声明和实例化,可以直接在Java程序片和Java表达式部分使用,称这样的对象为JSP内置对象。JSP内置对象由Web服务器负责实现和管理,JSP自带了9个功能强大的内置对象,共分为4类。 (1) 与Input/Output有关的内置对象: 与Input/Output有关的内置对象包括request、response和out,该类对象主要用来作为客户端和服务器之间通信的桥梁。request对象表示客户端对服务器发送的请求; response对象表示服务器对客户端的响应; 而out对象负责把处理结果输出到客户端。 (2) 与Context有关的内置对象: 与Context(上下文)有关的内置对象包括session、application和pageContext。其中session对象表示浏览器与服务器会话的上下文环境; application对象表示应用程序(Web应用)的上下文环境; pageContext对象表示当前JSP页面的上下文环境。 (3) 与Servlet有关的内置对象: 与Servlet有关的内置对象包括page和config。page对象表示JSP文件转换为Java文件后的Servlet对象; config对象表示JSP文件转换为Java文件后的Servlet的ServletConfig对象。 (4) 与Error有关的内置对象: 与Error有关的内置对象只有一个exception对象。当JSP网页有错误时将产生异常,该对象用来处理这个异常。 按照1.2.2节的操作步骤创建一个Web项目ch3,并为ch3添加Tomcat依赖。本章涉及的JSP页面保存在ch3项目的web目录中。 扫一扫 视频讲解 3.1request对象 request对象的类型为jakarta.servlet.http.HttpServletRequest。当客户端请求一个JSP页面时,JSP页面所在的服务器将客户端发出的所有请求信息封装在内置对象request中,因此使用该对象就可以获取客户端提交的信息。 3.1.1request对象的常用方法 request对象的常用方法如表3.1所示。 表3.1request对象的常用方法 序号方法功 能 说 明 1Object getAttribute(String name) 返回指定属性的属性值 2Enumeration getAttributeNames() 返回所有可用属性名的枚举 3String getCharacterEncoding() 返回字符编码方式 4int getContentLength() 返回请求体的字节数 5String getContentType() 返回请求体的MIME类型 6ServletInputStream getInputStream()返回请求体中一行的二进制流 7String getParameter(String name)返回name参数的值 8Enumeration getParameterNames() 返回可用参数名的枚举 9String[] getParameterValues(String name) 返回包含name参数的所有值的数组 10String getProtocol() 返回请求用的协议类型及版本号 11String getServerName() 返回接受请求的服务器的主机名 12int getServerPort()返回服务器接受此请求所用的端口号 13String getRemoteAddr() 返回发送此请求的客户端的IP地址 14String getRemoteHost() 返回发送此请求的客户端的主机名 15void setAttribute(String key,Object obj) 设置属性的值 16String getRealPath(String path) 返回一个虚拟路径的真实路径  用request对象获取客户端提交的信息 1) String getParameter(String name) 该方法以字符串的形式返回客户端传来的某个参数的值,该参数名由name指定。 【例31】调用getParameter(String name)方法获取表单信息。 example3_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_1.jsp/title /head body form action="getValue.jsp" input type="text" name="userName"/ input type="submit" value="提交"/ /form /body /html getValue.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titlegetValue.jsp/title /head body % String name = request.getParameter("userName"); //userName为example3_1.jsp页面中的表单参数名 out.println(name); % /body /html 2) String[] getParameterValues(String name) 该方法以字符串数组的形式返回客户端向服务器传递的指定参数名的所有值。 【例32】调用getParameterValues(String name)方法获取表单信息。 example3_2.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_2.jsp/title /head body form action="getValues.jsp" 选择你去过的城市:br/ input type="checkbox" name="cities" value="北京"/北京 input type="checkbox" name="cities" value="上海"/上海 input type="checkbox" name="cities" value="西安"/西安 input type="submit" value="提交"/ /form /body /html getValues.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titlegetValues.jsp/title /head body 您去过的城市:br % String yourCities[] = request.getParameterValues("cities"); for(int i = 0; i yourCities.length; i ++){ out.println(yourCities[i] +"br"); } % /body /html  NullPointerException异常 如果不选择example3_2.jsp页面中的城市,直接单击“提交”按钮,那么getValues.jsp页面就会提示出现NullPointerException异常。为了避免在运行时出现NullPointerException异常,在getValues.jsp页面中使用以下代码: if(yourCities != null){ for(int i = 0;i yourCities.length; i ++){ out.print(yourCities[i] + "br"); } } 3.1.2用request对象存取数据 request对象可以通过void setAttribute(String key, Object obj)方法将参数obj指定的对象保存到request对象中,key为所保存的对象指定一个关键字。若保存的两个对象关键字相同,则先保存的对象被清除。 request对象可以通过Object getAttribute(String key)方法获取请求域(例如forward转发)中的关键字为key的对象(属性值)。 在实际应用中,request对象经常用于存储、传递本次请求的处理结果。 【例33】编写两个JSP页面example3_3.jsp和example3_3_1.jsp,在example3_3.jsp页面中输入一个整数提交给example3_3_1.jsp页面求平方。当输入值为非整数时,在example3_3_1.jsp页面中使用request对象的setAttribute(String key, Object obj)方法将错误消息存储到request对象中,同时使用forward动作标记转发到example3_3_1.jsp页面并显示错误消息。 example3_3.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_3.jsp/title /head body % //从请求域中获取errorMsg属性的错误消息 String error = (String)request.getAttribute("errorMsg"); if(error != null){ out.print("font color='red'" + error + "/font"); } % form action="example3_3_1.jsp" 输入一个整数求平方: input type="text" name="mynumber" br input type="submit" value="提交" /form /body /html example3_3_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_3_1.jsp/title /head body % String number = request.getParameter("mynumber"); int toNumber = 0; try{ toNumber = Integer.parseInt(number); out.print(toNumber*toNumber); }catch(Exception e){ request.setAttribute("errorMsg", "请输入一个整数!"); % jsp:forward page="example3_3.jsp"/jsp:forward % } % /body /html 在例33中,错误消息被以请求域属性的形式保存到request对象中,并通过请求转发的方式将请求对象再转发给example3_3.jsp,在example3_3.jsp页面中便可以从request对象中获取到属性值,从而实现了错误消息在一次request请求范围内传递。 3.1.3中文乱码问题 Java的内核和class文件是基于unicode的,这使Java程序具有良好的跨平台性,但也产生了一些中文乱码问题。 如果在例31的 example3_1.jsp页面的文本框中输入中文姓名,那么getValue.jsp页面获得的姓名可能是乱码。如果出现中文乱码,可以使用request对象的setCharacterEncoding(String code)方法设置统一字符编码,其中参数code以字符串形式传入要设置的编码格式,但这种方法仅对于提交方式是post的表单(表单默认的提交方式是get)有效。例如,使用该方法解决例31中的getValue.jsp页面出现的中文乱码问题,需要完成以下两件事。 首先将example3_1.jsp中的表单提交方式改为“post”,具体代码如下: form action="getValue.jsp" method="post" 然后在getValue.jsp中获取表单信息之前设置统一编码,具体代码如下: request.setCharacterEncoding("UTF-8"); 在使用该方法解决中文乱码问题时,接受参数的每个页面都需要执行request.setCharacterEncoding("UTF8")。为了避免每个页面都编写request.setCharacterEncoding("UTF8"),可以使用过滤器对所有JSP页面进行编码处理。过滤器将在本书的第6章中讲解。 3.1.4实践环节——获取客户端的基本信息 编写一个JSP页面practice3_1.jsp,在该页面中使用request的方法获取客户端的IP地址、客户机名称、服务器名称以及服务器端口号。 扫一扫 视频讲解 3.2response对象 当客户端请求服务器的一个页面时会提交一个HTTP请求,服务器收到请求后返回HTTP响应。request对象对请求信息进行封装,与request对象对应的对象是response对象。response对象的类型为jakarta.servlet.http.HttpServletResponse,对客户端的请求做出动态响应。动态响应通常有动态改变contentType属性值、设置响应表头和response重定向。 3.2.1动态改变contentType属性值 JSP页面用page指令标记设置了页面的contentType属性值, response对象按照此属性值的方式对客户端做出响应。在page指令标记中只能为contentType属性指定一个值。如果想动态改变contentType属性值,换一种方式来响应客户端,可以让response对象调用setContentType(String s)方法来重新设置contentType的属性值。 【例34】编写一个JSP页面example3_4.jsp,客户端通过单击页面上的不同按钮可以改变页面响应的MIME类型。当单击Word按钮时,JSP页面动态改变contentType属性值为application/msword,在内置的浏览器中启用本地的Word软件来显示当前页面内容; 当单击Excel按钮时,JSP页面动态改变contentType属性值为application/vnd.msexcel,浏览器启用本地的Excel软件来显示当前页面内容。页面效果如图3.1所示。 图3.1例34的效果图 example3_4.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_4.jsp/title /head body form action="" method="post" p我们在学习使用response动态改变contentType属性值 p input type="submit" value="Word" name="submit" input type="submit" value="Excel" name="submit" % String str = request.getParameter("submit"); if ("Word".equals(str)) { //response调用setContentType()方法设置MIME类型为application/msword response.setContentType("application/msword"); } else if ("Excel".equals(str)) { //response调用setContentType()方法设置MIME类型为application/vnd.ms-excel response.setContentType("application/vnd.ms-excel"); } % /form /body /html 3.2.2设置响应表头 response对象可以通过setHeader(String name,String value)方法设置指定名字的HTTP文件头值,以此来操作HTTP文件头。如果希望某页面每3秒钟刷新一次,那么在该页面中添加如下代码: response.setHeader("refresh","3"); 大家有时候希望几秒钟后从当前页面自动跳转到另一个页面。比如打开one.jsp页面3秒钟后自动跳转到another.jsp页面(one.jsp和another.jsp在同一个Web服务目录下)。这该如何实现呢?只需要为one.jsp设置一个响应头即可,也就是在one.jsp页面中添加如下代码: response.setHeader("refresh","3;url=another.jsp"); 【例35】编写一个JSP页面example3_5.jsp,在该页面中使用response对象设置一个响应头“refresh”,其值是“3”。那么用户收到这个响应头后,该页面会每3秒钟刷新一次。 example3_5.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% %@ page import="java.util.*" % !DOCTYPE html html head meta charset="UTF-8" titleexample3_5.jsp/title /head body h2该页面每3秒钟刷新一次/h2 p现在的秒钟时间是: % Calendar c = Calendar.getInstance(); out.print("" + c.get( Calendar.SECOND)); response.setHeader("refresh","3"); % /body /html 3.2.3response重定向 当需要将客户端引导至另一个页面时,可以使用response对象的sendRedirect(String url)方法实现客户端的重定向。例如客户端输入的表单信息不完整或有误,应该再次被重定向到输入页面。 【例36】编写两个JSP页面login.jsp和validate.jsp,如果在login.jsp页面中输入正确的密码“nihao2023”,单击“提交”按钮后提交给validate.jsp页面,如果输入不正确,重新定向到login.jsp页面。先运行login.jsp页面,页面效果如图3.2所示。 图3.2例36的效果图 login.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titlelogin.jsp/title /head body form action="validate.jsp" method="post" name=form p 输入密码: br input type="password" name="pwd"/ input type="submit" value="提交" /form /body /html validate.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titlevalidate.jsp/title /head body % String str = request.getParameter("pwd"); if (!"nihao2023".equals(str)) { response.sendRedirect("login.jsp"); } else { out.print("2023年是蛮拼的一年!"); } % /body /html response对象的sendRedirect()方法在客户端的浏览器中工作,Web服务器要求浏览器重新发送一个到被定向页面的请求。在浏览器的地址栏上将出现重定向页面的URL,且为绝对路径。 forward动作标记也可以实现页面的跳转,例如。使用forward动作标记与使用response对象调用sendRedirect()不同,两者的比较如下: (1) forward动作标记为服务器端跳转,浏览器的地址栏不变; sendRedirect()为客户端跳转,浏览器的地址栏改变为新页面的URL。 (2) 执行到forward动作标记出现处停止当前JSP页面的继续执行,转向动作标记中page属性指定的页面; sendRedirect()在所有代码执行完毕之后再跳转。 (3) 使用forward动作标记,request请求信息能够保留到下一个页面; 使用sendRedirect()不能保留request请求信息。 forward动作标记传递参数的格式如下: jsp:forward page="info.jsp" jsp:param name="no" value="001"/ jsp:param name="age" value="18"/ /jsp:forward response对象的sendRedirect()传递参数的格式如下: response.sendRedirect("info.jsp?sno=001&sage=18") ; 3.2.4实践环节—— 登录验证 编写3个JSP页面login_1.jsp、server.jsp和loginSuccess.jsp。在login_1.jsp页面中输入用户名和密码,单击“提交”按钮将输入的信息提交给server.jsp页面。在server.jsp页面中进行登录验证: 如果输入正确(用户名为“zhangsan”,密码为“123”),提示“成功登录,3秒钟后进入loginSuccess.jsp页面”,如果输入不正确,重新定向到login_1.jsp页面。先运行login_1.jsp页面,页面效果如图3.3所示。 图3.33.2.4实践环节的效果图 3.3out对象 out对象的类型为jakarta.servlet.jsp.JspWriter,它是一个输出流,用来向客户端的浏览器输出数据。out对象的常用方法如表3.2所示。 表3.2out对象的常用方法 序号方法功 能 说 明 1void clear() 清除缓冲区的内容 2void clearBuffer()清除缓冲区的当前内容 3void flush() 清空流 4int getBufferSize() 返回缓冲区的字节数,如果不设缓冲区则返回0 5int getRemaining() 返回缓冲区的剩余大小 6boolean isAutoFlush()返回缓冲区满时是自动清空还是抛出异常 7void close()关闭输出流 8void print() 输出各种数据类型 9void newLine() 输出一个换行符 【例37】编写一个页面example3_7.jsp,在该页面中使用out对象输出信息。 example3_7.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% %@ page import="java.util.Date"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_7.jsp/title /head body % int myNumber = 1000; Date myDate = new Date(); out.print(myNumber); out.print("br"); out.print(myDate); % /body /html 扫一扫 视频讲解 3.4session对象 浏览器与Web服务器之间使用HTTP进行通信。HTTP是一种无状态协议,客户端向服务器发出请求(request),服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的相关信息,所以服务器必须采取某种手段来记录每个客户端的连接信息。Web服务器可以使用内置对象session来存放有关连接的信息,session对象的类型为jakarta.servlet.http.HttpSession。session对象指的是客户端与服务器端的一次会话,从客户端连到服务器端的一个Web应用程序开始,直到客户端与服务器端断开为止。 3.4.1session对象的ID Web服务器会给每一个用户自动创建一个session对象,为每个session对象分配一个唯一标识的String类型的session ID,这个ID用于区分其他用户。这样每个用户都对应着一个session对象,不同用户的session对象互不相同。session对象调用getId()方法可以获取当前session对象的ID。 【例38】编写3个JSP页面example3_8_1.jsp、example3_8_2.jsp和example3_8_3.jsp,其中, example3_8_2.jsp存放在目录tom中,example3_8_3.jsp存放在目录cat中。用户首先访问example3_8_1.jsp页面,从该页面链接到example3_8_2.jsp页面,然后从example3_8_2.jsp页面链接到example3_8_3.jsp,效果如图3.4所示。 图3.4获取session对象的ID example3_8_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_8_1.jsp/title /head body 年轻人如何养生呢?brbr 先看看Web服务器给我分配的session对象的ID: % String id = session.getId(); % br %=id % brbr 单击链接去a href="tom/example3_8_2.jsp"吃睡篇/a看看吧! /body /html example3_8_2.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_8_2.jsp/title /head body 欢迎您进入养生之font size=5吃睡篇/font!brbr 先看看Web服务器给我分配的session对象的ID: % String id = session.getId(); % br %=id % brbr 吃,不忌口,五谷杂粮、蔬菜水果通吃不挑食br 睡,早睡早起不熬夜brbr 单击链接去a href="../cat/example3_8_3.jsp"运动篇/a看看吧! /body /html example3_8_3.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_8_3.jsp/title /head body 欢迎您进入养生之font size=5运动篇/font!brbr 先看看Web服务器给我分配的session对象的ID: % String id = session.getId(); % br %=id % brbr 动,坚持运动——这一点年轻人很多都做不好,br高兴起来就拼命打球,懒起来拼命睡觉,不好! br 总之,生活规律化,坚持长期运动brbr 单击链接去a href="../example3_8_1.jsp"首页/a看看吧! /body /html 从例38各个页面的运行结果来看,一个用户在同一个Web服务中只有一个session对象,当用户访问相同Web服务的其他页面时,Web服务器不会再重新分配session对象,直到用户关闭浏览器或这个session对象结束了它的生命周期。当用户重新打开浏览器访问该Web服务时,Web服务器为该用户再创建一个新的session对象。 需要注意的是,同一用户在多个不同的Web服务中所对应的session对象是不同的,一个Web服务对应一个session对象。 3.4.2用session对象存取数据 使用session对象可以保存用户在访问某个Web服务期间的有关数据。有关数据处理的方法如下。 (1) public void setAttribute(String key, Object obj): 将参数obj指定的对象保存到session对象中,key为所保存的对象指定一个关键字。若保存的两个对象的关键字相同,则先保存的对象被清除。 (2) public Object getAttribute(String key): 获取session中关键字是key的对象。 (3) public void removeAttribute(String key): 从session中删除关键字key所对应的对象。 (4) public Enumeration getAttributeNames(): 产生一个枚举对象,该枚举对象可以使用nextElements()方法遍历session中各个对象所对应的关键字。 【例39】使用session对象模拟在线考试系统。编写3个JSP页面example3_9_1.jsp、example3_9_2.jsp和example3_9_3.jsp,在example3_9_1.jsp页面中考试,在example3_9_2.jsp页面中显示答题结果,在example3_9_3.jsp页面中计算并公布考试成绩。首先运行example3_9_1.jsp页面,效果如图3.5所示。 图3.5使用session对象模拟在线考试系统 example3_9_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_9_1.jsp/title /head body form action="example3_9_2.jsp" method="post" 考号: input type="text" name="id"/ p 一、单项选择题(每题2分) br/br/ 1.下列哪个方法是获取session中关键字是key的对象()。 br/ input type="radio" name="one" value="A"/ A.public void setAttribute(String key, Object obj)br/ input type="radio" name="one" value="B"/ B.public void removeAttribute(String key)br/ input type="radio" name="one" value="C"/ C.public Enumeration getAttributeNames()br/ input type="radio" name="one" value="D"/ D.public Object getAttribute(String key)br/ /p p 二、判断题(每题2分) br/br/ 1.同一用户在多个Web服务中,所对应的session对象是互不相同的。 br/ input type="radio" name="two" value="True"/ True input type="radio" name="two" value="False"/ False /pbr/ input type="submit" value="提交" name=submit input type="reset" value="重置" name=reset /form /body /html example3_9_2.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_9_2.jsp/title /head body form action="example3_9_3.jsp" method="post" % //考号 String id = request.getParameter("id"); //把考号id以"id"为关键字存储到session对象中 session.setAttribute("id", id); //单项选择题的第1题 String first = request.getParameter("one"); //把答案first以"one"为关键字存储到session对象中 session.setAttribute("one", first); //判断题的第1题 String second = request.getParameter("two"); //把答案second以"two"为关键字存储到session对象中 session.setAttribute("two", second); % 您的考号:%=id%br/ 一、单项选择题(每题2分) br/ 1.%=first% br/ 二、判断题(每题2分) br/ 1.%=second%br/ input type="submit" value="确认完毕"/ a href="example4_9_1.jsp"重新答题/a /form /body /html example3_9_3.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_9_3.jsp/title /head body % //获取考号 //获取session中关键字是id的对象(考号) String id = (String) session.getAttribute("id"); //计算成绩 int sum = 0; //如果单项选择题的第1题选中D选项,得2分 //获取session中关键字是one的对象(选择答案) String first = (String) session.getAttribute("one"); if ("D".equals(first)) { sum += 2; } //如果判断题的第1题选中True,得2分 //获取session中关键字是two的对象(判断答案) String second = (String) session.getAttribute("two"); if ("True".equals(second)) { sum += 2; } % 您的成绩公布如下: table border="1" tr th width="50%" 考号 /th th width="50%" 成绩 /th /tr tr td%=id%/td td align="right"%=sum%/td /tr /table /body /html 3.4.3session对象的生命周期 在某个Web服务中session对象的生命周期依赖于以下几个因素: (1) 用户是否关闭浏览器。 (2) session对象是否调用invalidate()方法。 (3) session对象是否达到设置的最长“发呆”时间。 与session对象的生命周期相关的方法如表3.3所示。 表3.3与session对象的生命周期相关的方法 序号方法功 能 说 明 1long getCreationTime() 返回session的创建时间 2long getLastAccessedTime ()返回此session中客户端最近一次请求的时间 3int getMaxInactiveInterval() 返回两次请求的间隔时间(单位是秒) 4void invalidate() 使session失效 5boolean isNew()判断客户端是否已经加入服务器创建的session 6void setMaxInactiveInterval()设置两次请求的间隔时间(单位是秒) 【例310】编写一个JSP页面example3_10.jsp。如果用户是第一次访问该页面,会显示欢迎信息,并输出session对象允许的最长“发呆”时间、创建时间,以及session对象的id。在example3_10.jsp页面中,session对象使用setMaxInactiveInterval(int maxValue)方法设置最长“发呆”时间为10秒。如果用户两次刷新的间隔时间超过10秒,用户先前的session会被取消,用户将获得一个新的session对象。页面的运行效果如图3.6所示。 图3.6session生命周期示例的运行效果 example3_10.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html %@ page import="java.util.*"% %@ page import="java.text.*"% html head meta charset="UTF-8" titleexample3_10.jsp/title /head body % //session调用setMaxInactiveInterval(int n)方法设置最长"发呆"时间为10秒 session.setMaxInactiveInterval(10); //session调用isNew()方法判断session是否为新创建的 boolean flg = session.isNew(); if (flg) { out.println("欢迎您第一次访问当前Web服务。"); out.println("hr/"); } out.println("session允许的最长发呆时间为:" + session.getMaxInactiveInterval()+ "秒。"); //获取session对象被创建的时间 long num = session.getCreationTime(); //将整数转换为Date对象 Date time = new Date(num); //用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat对象 SimpleDateFormat matter = new SimpleDateFormat( "北京时间:yyyy年MM月dd日HH时mm分ss秒E。"); //得到格式化后的字符串 String strTime = matter.format(time); out.println("br/session的创建时间为:" + strTime); out.println("br/session的id为:" + session.getId() + "。"); % /body /html 从例310中可以看出,如果用户长时间不关闭浏览器,session对象也没有调用invalidate()方法,那么用户的session也可能消失。例如该例中的JSP页面如果在10秒钟之内不被访问,它先前创建的session对象就消失了,服务器又重新创建一个session对象。这是因为session对象达到了它的最长“发呆”时间。所谓“发呆”时间,是指用户对某个Web服务发出的两次请求之间的间隔时间。 用户对某个Web服务的JSP页面发出请求并得到响应,如果用户不再对该Web服务发出请求,比如不再操作浏览器,那么用户对该Web服务进入“发呆”状态,直到用户再次请求该Web服务时“发呆”状态结束。 Tomcat服务器允许用户的最长“发呆”时间为30分钟,用户可以通过修改Tomcat安装目录中conf文件夹下的配置文件web.xml,找到下面的片段,修改其中的默认值“30”,这样就可以重新设置各个Web服务目录下的session对象的最长“发呆”时间。这里的时间单位为分钟。 session-config session-timeout30/session-timeout /session-config 另外,用户也可以通过session对象调用setMaxInactiveInterval(int time)方法来设置最长“发呆”时间,参数的时间单位为秒。 3.4.4实践环节——购物车 客户到便民超市采购商品,在购物前需要登录会员卡号,购物时先将选购的商品放入购物车,再到柜台清点商品。请借助session对象模拟购物车,并存储客户的会员卡号和购买的商品的名称。会员卡号在输入以后可以修改,购物车中的商品可以查看。编写程序模拟上述过程。loginID.jsp实现会员卡号的输入,shop.jsp实现商品导购,food.jsp实现商品购物,count.jsp实现清点商品。本节实践环节的4个JSP页面都保存在practice4目录中,先运行loginID.jsp页面,运行效果如图3.7所示。 图3.7借助session对象模拟购物车 扫一扫 视频讲解 3.5application对象 3.5.1什么是application对象 不同用户的session对象互不相同,但有时用户之间可能需要共享一个对象,在Web服务器启动后就产生了这样一个唯一的内置对象application。application对象实现了jakarta.servlet.ServletContext接口。任何用户在访问同一个Web服务的各个页面时共享一个application对象,直到服务器关闭这个application对象才被取消。 3.5.2用application对象存取数据 application对象和session对象一样也可以进行数据的存取,处理数据的方法如下。 (1) public void setAttribute(String key, Object obj): 将参数obj指定的对象保存到application对象中,key为所保存的对象指定一个关键字。若保存的两个对象的关键字相同,则先保存的对象被清除。 (2) public Object getAttribute(String key): 获取application中关键字是key的对象。 (3) public void removeAttribute(String key): 从application中删除关键字key所对应的对象。 (4) public Enumeration getAttributeNames(): 产生一个枚举对象,该枚举对象可以使用nextElements()方法遍历application中各个对象所对应的关键字。 【例311】用application对象模拟“成语接龙”,用户通过example3_11_1.jsp向example3_11_2.jsp页面在提交四字成语example3_11_2.jsp页面在获取成语内容后用同步方法将该成语内容和以前的成语内容进行连接,然后将这些四字成语添加到application对象中。页面的运行效果如图3.8所示。 图3.8成语接龙 example3_11_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_11_1.jsp/title /head body h2四字成语接龙/h2 % //取出application中关键字是message的对象(成语内容) StringBuffer s = (StringBuffer)application.getAttribute("message"); if(s != null){ out.print(s.toString()); } else{ out.print("还没有词语,请您龙头开始!br"); } % form action="example3_11_2.jsp" method="post" 四字成语输入:input type="text" name="mes"/br input type="submit" value="提交"/ /form /body /html example3_11_2.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_11_2.jsp/title /head body %! StringBuffer message = new StringBuffer(""); ServletContext application; synchronized void sendMessage(String s){ application = getServletContext(); message.append(s + "-") ; //把成语内容message以"message"为关键字存储到application对象中 application.setAttribute("message", message); } % % request.setCharacterEncoding("UTF-8"); String content = request.getParameter("mes"); sendMessage(content); out.print("您的四字成语已经提交!3秒钟后回到成语页面,继续接龙!"); response.setHeader("refresh", "3;url=example3_11_1.jsp"); % /body /html 例311中的成语接龙方法sendMessage()为什么定义为同步方法?这是因为application对象对所有用户都是相同的,任何用户对该对象中存储的数据进行操作都会影响到其他用户。 如果客户端浏览不同的Web服务,将产生不同的application对象。同一个Web服务的所有JSP页面都共享一个application对象,即使浏览这些JSP页面的是不同的客户端也是如此。因此,保存在application对象中的数据不仅可以跨页面分享,还可以由所有用户共享。 有些Web服务器不能直接使用application对象,必须使用父类ServletContext声明这个对象,然后使用getServletContext()方法为application对象进行实例化。例如例311中example3_11_2.jsp页面的代码。 3.5.3实践环节——网站访客计数器 使用application对象实现网站访客计数器的功能。 3.6pageContext对象 pageContext对象即页面上下文对象,表示当前页面运行环境,用于获取当前JSP页面的相关信息,它的作用范围是当前JSP页面。pageContext对象的类型为jakarta.servlet.jsp.PageContext。pageContext对象可以访问当前JSP页面的所有内置对象,如表3.4所示。另外,pageContext对象提供了存取属性的方法,如表3.5所示。 表3.4pageContext对象获取内置对象的方法 序号方法功 能 说 明 1ServletRequest getRequest() 获取当前JSP页面的请求对象 2ServletResponse getResponse()获取当前JSP页面的响应对象 3HttpSession getSession() 获取和当前JSP页面有关的会话对象 4ServletConfig getServletConfig() 获取当前JSP页面的ServletConfig对象 5ServletContext getServletContext() 获取当前JSP页面的运行环境的application对象 6Object getPage()获取当前JSP页面的Servlet实体的page对象 7Exception getException()获取当前JSP页面的异常对象exception,这时此页面的page指令的isErrorPage属性要设置为true 8JspWriter getOut() 获取当前JSP页面的输出流对象out 表3.5pageContext对象提供的存取属性的方法 序号方法功 能 说 明 1Object getAttribute(String key, int scope) 获取范围为scope、关键字为key的属性对象 2void setAttribute(String key,Object value,int scope)以“键值”对的方式存储范围为scope的属性对象 3void removeAttribute(String key, int scope) 从scope范围移除关键字为key的属性对象 4Enumeration getAttributeNamesInScope(int scope) 从scope范围中获取所有属性对象对应的关键字 【例312】编写一个JSP页面example3_12.jsp,在该页面中使用pageContext对象添加和获取请求域属性值。 example3_12.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleInsert title here/title /head body % //添加页面域属性值 pageContext.setAttribute("pageKey", "页面域属性"); //获取页面域属性值 String pageValue = (String)pageContext.getAttribute("pageKey"); //添加请求域属性值 pageContext.getRequest().setAttribute("requestKey", "请求域属性"); //获取请求域属性值 String requestValue = (String)pageContext.getAttribute("requestKey", 2); out.print(pageValue + "br"); out.print(requestValue); % /body /html 3.7page对象 page对象是一个与Servlet有关的内置对象,它表示JSP文件转译后的Servlet对象,代表JSP页面本身,即this,因此它可以调用Servlet类所定义的方法。page对象的类型为jakarta.servlet.jsp.HttpJspPage,在实际应用中很少在JSP页面中使用page对象。 【例313】编写一个JSP页面example3_13.jsp,在该页面中使用page指令的info属性设置页面的说明信息,并分别使用this和page对象获取页面的说明信息。 example3_13.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" info="page内置对象测试"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_13.jsp/title /head body 使用this获取本页面的说明信息:%=this.getServletInfo() %br 使用page对象获取本页面的说明信息:%=((HttpJspPage)page).getServletInfo() % /body /html 3.8config对象 config对象即页面配置对象,表示当前JSP页面转译后的Servlet的ServletConfig对象,它存储着一些初始数据。config对象实现了jakarta.servlet.ServletConfig接口。config对象和page对象一样,也很少被用到。 【例314】编写一个JSP页面example3_14.jsp,在该页面中使用config对象获取当前JSP页面转译后的Servlet对象名。 example3_14.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_14.jsp/title /head body !--获取当前JSP页面转译后的Servlet对象名-- %=config.getServletName()% /body /html 3.9exception对象 exception对象是一个与Error有关的内置对象,表示JSP页面产生的异常。如果一个JSP页面需要使用此对象,必须将页面中page指令的isErrorPage属性设置为true,否则无法编译。 【例315】编写两个JSP页面example3_15.jsp和example3_15_1.jsp。在example3_15.jsp页面中使用语句“exception.printStackTrace(response.getWriter());”输出JSP页面产生的异常信息; 在example3_15_1.jsp页面中产生数组越界异常,并设置该页面page指令的errorPage属性值为example3_15.jsp。 example3_15.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_15.jsp/title /head body % exception.printStackTrace(response.getWriter()); % /body /html example3_15_1.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" errorPage="example4_15.jsp"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_15_1.jsp/title /head body % int a[] = {1,2,3,4,5}; for(int i = 0; i = 5; i++){ out.print(a[i]); } % /body /html 运行example3_15_1.jsp页面,效果如图3.9所示。 图3.9example3_15_1.jsp页面的运行效果 3.10JSP的4种作用域 对象的作用域就是对象的生命周期和可访问性,在JSP中有4种作用域,即页面域、请求域、会话域和应用域。  页面域 页面域(page scope)的生命周期为页面执行期间。存储在页面域中的对象只能在它所在的页面被访问。  请求域 请求域(request scope)的生命周期为一次请求过程,包括请求被转发(forward)或者被包含(include)的情况。存储在请求域中的对象只有在此次请求过程中才可以被访问。  会话域 会话域(session scope)的生命周期为某个客户端与服务器所连接的时间。客户端在第一次访问服务器时创建会话,在会话过期或用户主动退出后结束会话。存储在会话域中的对象在整个会话期间(可以包含多次请求)都可以被访问。  应用域 应用域(application scope)的生命周期为从服务器开始执行服务到服务器关闭为止,是4个作用域中时间最长的。存储在应用域中的对象在整个应用程序运行期间可以被所有JSP和Servlet共享访问。 JSP的4种作用域分别对应pageContext、request、session和application 4个内置对象,这4个内置对象都可以通过setAttribute(String key, Object value)方法存储数据,通过getAttribute(String key)方法获取数据。 【例316】编写一个JSP页面example3_16.jsp,在该页面中使用pageContext、session和application对象分别实现页面域、会话域和应用域的页面访问统计情况。 example3_16.jsp的代码如下: %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"% !DOCTYPE html html head meta charset="UTF-8" titleexample3_16.jsp/title /head body % int pageSum = 1; int sessionSum = 1; int applicationSum = 1; //页面域的计数 if(pageContext.getAttribute("pageCount") != null){ pageSum = Integer.parseInt(pageContext.getAttribute("pageCount").toString()); pageSum++; } pageContext.setAttribute("pageCount", pageSum); //会话域的计数 if(session.getAttribute("sessionCount") != null){ sessionSum = Integer.parseInt(session.getAttribute("sessionCount").toString()); sessionSum++; } session.setAttribute("sessionCount", sessionSum); //应用域的计数 if(application.getAttribute("applicationCount") != null){ applicationSum = Integer.parseInt(application.getAttribute("applicationCount").toString()); applicationSum++; } application.setAttribute("applicationCount", applicationSum); % p页面域访问统计:%=pageSum%/p p会话域访问统计:%=sessionSum%/p p应用域访问统计:%=applicationSum%/p /body /html 第一次访问example3_16.jsp页面,运行效果如图3.10所示。 多次刷新浏览器窗口,运行效果如图3.11所示。 图3.10第一次访问example3_16.jsp页面 图3.11多次刷新example3_16.jsp页面 打开另一个浏览器窗口,再次访问example3_16.jsp页面,运行效果如图3.12所示。 图3.12打开新的浏览器窗口访问example3_16.jsp页面 从图3.10~图3.12的运行效果可以看出,pageContext域的访问范围为当前JSP页面,因此访问计数始终为1; session域的访问范围为当前浏览器与服务器的会话,因此刷新页面访问计数会累加,但新打开浏览器窗口时会新建一个会话,计数又从1开始; application域的访问范围为整个应用,因此只要服务器不停止运行,计数会不断累加。 3.11本章小结 本章重点介绍了request、session和application对象, request、session和application对象的范围是逐个增加的: request只在一个请求范围内; session在客户端与服务器会话范围内; application则在整个服务器的运行过程中。 习 题 3