3.3 隐式对象
3.3 隐式对象
概述
Servlet
容器会传递几个对象给它运行的Servlet
。例如,可以通过Servlet
的service
方法拿到HttpServletRequest
和HttpServletResponse
对象,以及可以通过init
方法访问到ServletConfig
对象。此外,可以通过调用HttpServletRequest
对象的getSession
方法拿到HttpSession
对象。
JSP隐式对象
在JSP
中,可以通过使用隐式对象来访问上述对象。JSP
隐式对象如下表所示。
对象 | 类型 |
---|---|
request |
javax.servlet.http.HttpServletRequest |
response |
javax.servlet.http.HttpServletResponse |
out |
javax.servlet.jsp.JspWriter |
session |
javax.servlet.http.HttpSession |
application |
javax.servlet.ServletContext |
config |
javax.servlet.ServletConfig |
pageContext |
javax.servlet.jsp.PageContext |
page |
javax.servlet.jsp.HttpJspPage |
exception |
java.lang.Throwable |
request隐式对象
该隐式对象代表Servlet/JSP
容器传递给Servlet
服务方法的HttpServletRequest
对象。可以将request
理解为一个指向HttpServletRequest
对象的引用变量。下面的代码示例,从HttpServletRequest
对象中返回userName
参数值:
1 | <% |
pageContext隐式对象
获取Servlet相关对象
pageContext
提供了有用的上下文信息,并且通过其方法可以访问各种Servlet
相关对象,如getRequest
、getResponse
、getServletContext
、getServletConfig
和getSession
。当然,这些方法在脚本中不是非常有用的,因为可以更直接地通过隐式对象request
、response
、session
和application
等来直接访问。
获取和设置属性
PageContext
中提供了另一组有趣的方法:用于获取和设置属性的方法,也就是getAttribute
方法和setAttribute
方法。属性值可被存储在4个范围之一:页面
、请求
、会话
和应用程序
。
- 页面范围是最小范围,这里存储的属性只在同一个
JSP
页面之中可用。 - 请求范围是指属性存储在当前的
ServletRequest
里面。 - 会话范围指属性存储在当前的
HttpSession
里面。 - 应用程序范围指属性存储在应用的
ServletContext
里面。
setAttribute方法
PageContext
的setAttribute
方法签名如下:
1 | public abstract void setAttribute(java.lang.String name, |
其中,scope
的取值范围为PageContext
对象的中存储静态int
值:PAGE_SCOPE
、REQUEST_ SCOPE
、SESSION_SCOPE
和APPLICATION_SCOPE
。
把属性存储到页面范围
若要保存一个属性到页面范围,可以直接使用setAttribute
重载方法:
1 | public abstract void setAttribute(java.lang.String name, |
实例
如下脚本将一个属性保存到ServletRequest
中:
1 | <% |
同样效果的Java代码如下:
1 | <% |
out隐式对象
隐式对象out
引用了一个javax.servlet.jsp.JspWriter
对象,这类似于你在调用HttpServletResponse
的getWriter
方法时得到的java.io.PrintWriter
对象。可以通过调用它的print
方法将消息发送到浏览器。例如:
1 | out.println("Welcome"); |
Demo
implicitObjects.jsp
页面:
1 | <%@page import="java.util.Enumeration"%> |
浏览器显示效果如下:
在浏览器中具体看到的内容,取决于所使用的浏览器及其环境。
注意,在默认情况下,JSP
编译器会将JSP
页面的内容类型设为text/html
。如果要使用不同的类型,则需要
通过调用response.setContentType()
或者使用页面指令来设置内容类型。例如,下面就是将内容类型设置为text/json
:
1 | response.setContentType("text/json"); |
3.2 注释
3.2 注释
JSP页面中的两种注释
在开发中为JSP
页面添加注释是一个良好的习惯。JSP
支持两种不同的注释格式:
(1)JSP
注释。该注释记录了在JSP
页面中做了什么。JSP
注释不会发送到客户端.
(2)HTML/XHTML
注释。这些注释将会发送到浏览器上.
JSP注释
JSP
注释以“<%--
”开始,以“--%>
”结束。下面是一个例子:
1 | <%-- JSP注释 --%> |
注意 JSP
注释不会被发送到浏览器端,也不可以嵌套。
HTML注释
HTML/XHTML
注释语法如下:
1 | <!-- HTML注释 --> |
一个HTML/XHTML
注释不会被容器处理,会原样发送给浏览器。HTML/XHTML
注释的一个用途是用来确定JSP
页面本身。
1 | <!-- 这个是 /jsp/store/displayProducts.jsp 的输出 --> |
尤其是在运行有多个JSP
片段的应用时,会特别有用。开发人员可以根据HTML
源代码中的HTML
注释,找出是哪一个JSP
页面或片段产生了这些相应的HTML
片段。
3.1 JSP概述
3.1 JSP概述
JSP相较Servlet的优点
**JSP
页面本质上是一个Servlet
**。然而,用JSP
页面开发比使用Servlet
更容易,主要有两个原因。
- 首先,不必编译
JSP
页面; - 其次,
JSP
页面是一个以.jsp
为扩展名的文本文件,可以使用任何文本编辑器来编写它们。
JSP在哪里运行
JSP
页面在JSP
容器中运行,一个Servlet
容器通常也是JSP
容器。例如,Tomcat
就是一个Servlet/JSP
容器。
在JSP第一次被请求时 容器要做什么
当一个JSP
页面第一次被请求时,Servlet/JSP
容器主要做以下两件事情:
(1)把JSP
页面转换成JSP
页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage
接口或其子接口javax.servlet.jsp.HttpJspPage
的Java类。而javax.servlet.jsp.JspPage
接口又是javax.servlet.Servlet
的子接口,这使得**每一个JSP
页面都是一个Servlet
**。该实现类的类名由Servlet/JSP
容器生成。如果出现转换错误,则相关错误信息将被发送到客户端。
(2)如果转换成功,Servlet/JSP
容器随后编译该Servlet
类,并装载和实例化该类,并像其他正常的Servlet
一样执行生命周期操作。
在JSP页面的后续请求时 容器要做什么
对于同一个JSP
页面的后续请求,Servlet/JSP
容器会先检查JSP
页面是否被修改过。
- 如果该
JSP
页面被修改过,则该JSP
页面会被重新转换为Servlet
、然后编译并执行。 - 如果该
JSP
页面没有被修改过,则执行已经在内存中的JSP Servlet
。
这样一来,一个JSP
页面的第一次调用的实际花费总比后续调用的花费多,因为第一次调用涉及转换和编译。
如何减少第一次调用JSP页面时的花费
为了解决这个问题,可以执行下列动作之一:
- 配置应用程序,使所有的
JSP
页面在应用程序启动时被调用,也就是在引用程序启动时就转换和编译,而不是在第一次请求时被调用。 - 预编译
JSP
页面,并将其部署为Servlet
。
JSP自带的API包
JSP
自带的API
包含4个包,如下所示:
javax.servlet.jsp
。这个包包含了用于Servlet/JSP
容器将JSP
页面翻译为Servlet
的核心类和接口。其中的两个重要成员是JspPage
和HttpJspPage
接口。所有的JSP
页面实现类必须实现JspPage
或HttpJspPage
接口。在HTTP
环境下,实现HttpJspPage
接口是显而易见的选择。javax.servlet.jsp.tagext
。这个包中包含了用于开发自定义标签的类型。javax.el
。这个包中提供了统一表达式语言的API
。javax.servlet.jsp.el
。这个包提供了一组必须由Servlet/JSP
容器支持,以便在JSP
页面中使用表达式语言的类。
除了javax.servlet.jsp.tagext
,我们很少直接使用JSP API
。事实上,编写JSP
页面时,我们更关心Servlet API
,而非JSP API
。当然,我们还需要掌握JSP
语法,本章后续会进一步说明。开发JSP
容器或JSP
编译器时,JSP API
已被广泛使用。
可以在以下网址查看JSP API:
https://docs.oracle.com/javaee/7/api/index.html?javax/servlet/jsp/package-summary.html
模板数据和语法元素
JSP
页面可以包含模板数据和语法元素。这里,语法元素是一些具有特殊意义的JSP
转换符。例如,“<%
”是一个元素,因为它表示在JSP
页面中的Java
代码块的开始。“%>
”也是一个元素,因为它是Java
代码块的结束符。除去语法元素外的一切是模板数据。模板数据会原样发送给浏览器。例如,**JSP
页面中的HTML
标记和文字都是模板数据**。
Demo
第一个JSP页面
下面是一个名为welcome.jsp的JSP页面。这个页面仅仅向用户发送一个Welcome。
1 | <html> |
在Tomcat中,welcome.jsp页面在第一次请求时被翻译成名为welcome_jsp
的Servlet。你可以在Tomcat工作目录下的子目录中找到生成的Servlet,该Servlet继承
自org.apache.jasper. runtime.HttpJspBase抽象类,并实现了javax.servlet.jsp.HttpJspPage接口,org.apache.jasper. runtime.HttpJspBase继承于javax.servlet.http.HttpServlet类。
下面是为welcome.jsp生成的Servlet。
JSP页面对应的Servlet
JSP页面会被编译成Servlet,并发布到Tomcat安装目录下的\work\Catalina\localhost\项目名\包名\类名_jsp.java
,我这里是D:\dev\apache-tomcat-8.5.35\work\Catalina\localhost\app03a\org\apache\jsp\welcome_jsp.java
,打开这个文件,内容如下:
1 | /* |
JSP实现类的主体方法
正如我们在上面的代码中看到的,JSP
页面的主体是_jspService
方法,该方法中响应输出如下:
1 | public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) |
这个_jspService
方法被定义在HttpJspPage
,并被HttpJspBase
的service
方法调用。HttpJspBase
类中的service
方法如下所示:
1 | public final void service(HttpServletRequest request, |
一个JSP
页面不同于一个Servlet
的另一个方面是,JSP
页面不需要添加注解或在部署描述符中配置来映射URL
。在应用程序目录中的每一个JSP
页面可以直接在浏览器中输入路径页面访问。
app03a
应用程序的结构非常简单,由一个空的WEB-INF
目录和welcome.jsp
页面构成,如下图所示:
可以通过如下URL访问welcome.jsp页面:
http://localhost:8080/app03a/welcome.jsp
说明
添加新的JSP
界面后,无须重启Tomcat
。
显示今日日期
todaysDate.jsp
:
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" |
上面的JSP页面最终编译成:E:\apache-tomcat-8.5.35\work\Catalina\localhost\app03a\org\apache\jsp\todaysDate_jsp.java
这个Servlet,查看这个Servlet,可以看到_jspService
方法中的响应输出部分如下:
1 | public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) |
现在可以通过如下URL
访问todaysDate.jsp
页面:
http://localhost:8080/app03a/todaysDate.jsp
显示效果如下:todaysDate.jsp
页面发送了几个HTML
标签和字符串"当前日期: "
以及今天的日期到浏览器。
请注意两件事情。首先,Java
代码可以出现在JSP
页面中的任何位置,并通过<%
和%>
包括起来,这个<%...%>
块被称为scriplet
。其次,可以使用page
指令的import
属性导入在JSP
页面中使用的Java
类型,如果没有导入的类型,必须在代码中写Java
类的全路径名称。
第3章 Java Server Pages(JSP)概述
第3章 Java Server Pages(JSP)概述
Servlet的缺点
Servlet
有两个缺点是无法克服的:
- 写在
Servlet
中的所有HTML
标签必须写成Java
字符串的形式,这使得处理HTTP
响应报文的工作变得十分烦琐; - 所有的文本和
HTML
标记是硬编码,导致即使是表现层的微小变化,如改变背景颜色,也需要重新编译Servlet
。
JSP
Java Server Pages(JSP)
解决了上述两个问题。不过,JSP
不会取代Servlet
,相反,它们具有互补性。现代的Java
Web
应用会同时使用Servlet
和JSP
页面。
本章概述
本章介绍了JSP
技术,并讨论了在JSP
页面中,隐式对象的使用意见,3个语法元素(指令、脚本元素和动作),还讨论了错误处理。可以用标准语法或XML
语法编写JSP
。用XML
语法编写的JSP
页面被称为JSP
文档。由于很少用XML
语法编写JSP
,故本章不做介绍。在本章中,我们将主要学习JSP
标准语法。
2.4 Java Servlet 会话管理 通过HttpSession对象
2.4 Java Servlet 会话管理 通过HttpSession对象
在所有的会话跟踪技术中,HttpSession
对象是最强大和最通用的。一个用户可以有且最多有一个HttpSession
,并且不会被其他用户访问到。
谁创建HttpSession对象
HttpSession
对象在用户第一次访问网站的时候自动被创建。
如何获取HttpSession对象
你可以通过调用HttpServletRequest
的getSession
方法获取该对象。getSession
有两个重载方法:
1 | HttpSession getSession() |
没有参数的getSession
方法会返回当前的HttpSession
,若当前没有,则创建一个并返回。getSession(false)
返回当前HttpSession
,如当前不存在,则返回null
。getSession(true)
返回当前HttpSession
,若当前没有,则创建一个并返回,getSession(true)
同getSession()
的效果一致.
如何往HttpSession中存入数据
可以通过HttpSession
的setAttribute
方法将值放入HttpSession
,该方法的方法签名如下:
1 | void setAttribute(java.lang.String name, java.lang.Object value) |
请注意,不同于URL
重新、隐藏域或cookie
,放入到HttpSession
的值,是存储在内存中的,因此,不要往HttpSession
放入太多对象或放入大对象。
尽管现代的Servlet
容器在内存不够用的时候会将保存在HttpSessions
的对象转储到二级存储上,但这样有性能问题,因此小心存储。
HttpSession中的数据要满足的条件
此外,放到HttpSession
的值不限于String
类型,可以是任意实现java.io.Serializable
的java
对象,因为在内存不够用的时候,Servlet
容器会将这些对象放入文件或数据库中,当然你也可以将不支持序列化的对象放入HttpSession
,只是这样,当Servlet
容器进行序列化的时候会失败并报错。
调用setAttribute
方法时,若传入的name
参数此前已经使用过,则会用新值覆盖旧值。
从HttpSession对象中取出数据
通过调用HttpSession
的getAttribute
方法可以取回之前放入HttpSession
中的对象,该方法的签名如下:
1 | java.lang.Object getAttribute(java.lang.String name) |
从HttpSession对象中批量取出数据
HttpSession
还有一个非常有用的方法,名为getAttributeNames
,该方法会返回一个Enumeration
对象来迭代访问保存在HttpSession
中的所有值:
1 | java.util.Enumeration<java.lang.String> getAttributeNames() |
注意,所有保存在HttpSession
的数据不会被发送到客户端,不同于其他会话管理技术,**Servlet
容器为每个HttpSession
生成唯一的标识,并将该标识发送给浏览器**,或创建一个名为JSESSIONID
的cookie
,或者在URL
后附加一个名为jsessionid
的参数。在后续的请求中,浏览器会将标识提交给服务端,这样服务器就可以识别该请求是由哪个用户发起的。Servlet
容器会自动选择一种方式传递会话标识,无须开发人员介入。
提示:
- 隐藏域技术会往浏览器发送带隐藏域的表单.
Cookie
技术会往浏览器发送Cookie
.
获取Http对象的标识
可以通过调用 HttpSession
的getId
方法来读取该标识:
1 | java.lang.String getId() |
此外,HttpSession
.还定义了一个名为invalidate
的方法。该方法强制会话过期,并清空其保存的对象。默认情况下,HttpSession
会在用户不活动一段时间后自动过期,该时间可以通过部署描述符的 session-timeout
元素配置,若设置为30
,则会话对象会在用户最后一次访问30
分钟后过期,如果部署描述符没有配置,则该值取决于Servlet
容器的设定。
大部分情况下,你应该主动销毁无用的HttpSession
,以便释放相应的内存。
查看设置超时时间
可以通过调用HttpSession
的getMaxInactiveInterval
方法来查看会话多久会过期。该方法返回一个数字类型,单位为秒。调用setMaxInactiveInterval
方法来单独对某个HttpSession
设定其超时时间:
1 | void setMaxInactiveInterval(int seconds) |
若设置为0,则该HttpSession
永不过期。通常这不是一个好的设计,因为该 HttpSession
所占用的堆内存将永不释放,直到应用重加载或Servlet
容器关闭。
Demo
一个简单的购物Demo
,具有显示商品列表,商品详情页面,购物车页面.并且在商品详情页面提供添加到购物车的功能.
Product
商品类用来存储商品的信息,如下所示:
1 | package session.management.httpsession; |
购物车中光有商品而已是不行的,还要知道用户买了几个商品,商品
和对应的数量
构成了购物车中记录的数据结构。
ShoppingItem
1 | package session.management.httpsession; |
有了商品
,有了记录
,下面就来写功能代码了.
ShoppingCartServlet
1 | package session.management.httpsession; |
代码详解
创建并初始化商品列表
1 | // 商品列表 |
从商品列表中查找出一个商品
1 | // 根据商品ID,从商品列表中查找一个商品. |
匹配URL
1 | @WebServlet( |
doGet方法
1 | @Override |
显示产品列表
1 | private void sendProductList(HttpServletResponse response) |
显示商品详情
1 | // 显示商品详情 |
显示商品详情页,表单属性如下:
1 | <form method='post' action='addToCart'> |
当用户点击加入购物车
时,会使用POST方法把表单发送给.\addToCart
这个URL.
doPost方法
1 | @Override |
显示购物车
1 | // 显示购物车 |
2.3 Cookies
2.3 Cookies
URL重写
和隐藏域
仅适合保存无须跨越太多页面
的信息。如果需要在多个页面间传递信息,则以上两种技术实现成本高昂,因为你不得不在每个页面都进行相应处理。幸运的是,Cookies
技术可以帮助我们。Cookies
是一个很少的信息片段,可自动地在浏览器和Web
服务器间交互,因此cookies
可存储在多个页面间传递的信息。Cookie
作为HTTP header
的一部分,其传输由HTTP
协议控制。此外,你可以控制cookies
的有效时间。浏览器通常支持每个网站高达20个cookies
。Cookies
的问题在于用户可以通过改变其浏览器设置来拒绝接受cookies
。
要使用cookies
,需要熟悉javax.servlet.http.Cookie
类以及HttpServletRequest
和HttpServletResponse
两个接口。
创建Cookies
可以通过传递name
和value
两个参数给Cookie
类的构造函数来创建一个cookies
:
1 | Cookie cookie = new Cookie(name, value); |
如下是一个创建语言选择的cookie
示例:
1 | Cookie languageSelectionCookie = new Cookie("language", "Italian"); |
Cookie属性
创建完一个Cookie
对象后,你可以设置domain
、 path
和maxAge
属性。其中,maxAge 属性决定cookie何时过期。
服务器如何发送Cookie
要将cookie
发送到浏览器,需要调用 HttpServletResponse
的add
方法:
1 | httpServletResponse.addCookie(cookie); |
浏览器如何发送Cookie
浏览器在访问同一Web
服务器时,会将之前收到的 cookie
一并发送。 此外,Cookies
也可以通过客户端的javascript
脚本创建和删除。
服务器端如何读取浏览器提交的Cookie
服务端若要读取浏览器提交的cookie
,可以通过 HttpServletRequest
接口的getCookies
方法,该方法返回一个Cookie
数组,若没有Cookies
则返回null
.
查找Cookie
你需要遍历整个数组来查询某个特定名称的cookie
。如下为查询名为maxRecords
的cookie
的示例:
1 | //获取浏览器提交的所有Cookie数组 |
目前,还没有类似于getCookieByName
这样的方法来帮助简化工作。
删除Cookie
没有一个直接的方法来删除一个cookie
,你只能创建一个同名的Cookie
,并将maxAge
属性设置为0
,并添加到HttpServletResponse
接口中,覆盖掉原来的Cookie即可。如下为删除一个名为userName
的cookie
代码:
1 | Cookie cookie = new Cookie("userName", ""); |
demo
工具类
1 | package session.management.cookies; |
设置Cookie的Servlet
1 | package session.management.cookies; |
这个Servlet会把表单输入转换为Cookie发送到浏览器,对应的URL为http://localhost:8080/SessionManagement/setcookie,
当浏览器再次访问http://localhost:8080/SessionManagement/
这个Java项目下的资源时,浏览器会把之前服务器发给他的Cookie,再原封不动的提交给服务器。这样服务器就可以根据Cookie来进行响应的操作。
在上面代码中,服务器分别发送fruitNum
和cookieFontSize
这两个cookie给浏览器,下面将再创建两个Servlet来使用者连个Cookie。
填写表单,如下所示:
然后点击Set按钮,提交表单,得到提示如下:
根据Cookie显示数组中的元素
ShowFruitsByCookie
这个Servlet会根据fruitNum
这个Cookie的值,来显示打印数组中的元素,如下所示:
1 | package session.management.cookies; |
点击工具条上的Show Fruits
超链接,效果如下:
根据Cookie设置CSS
SetCSSByCookie
这个Servlet根据cookieFontSize
这个Cookie中的值来来设置元素的CSS样式,如下所示:
1 | package session.management.cookies; |
点击工具条上的Show CSS
超链接,显示效果如下:
2.1 URL重写
URL重写
URL重写
是一种会话跟踪技术,它将一个或多个键值对
添加到URL
的查询字符串中,每个键值对
通常为 key=value
形式,格式如下所示:
1 | url?key_1=value_1&key_2=value_2...&key_n=value_n |
注意,URL
和键值对
之间用问号?
来分割,键值对
之间用单与符号&
分割。URL
重写适合于在少数URL
之间传递信息
的情况下,它有如下限制:
URL
在某些浏览器上最大长度为2000字符,所以URL
重写可能无法传递大量数据。- 如果要传递
信息
到下一个资源,需要将信息
插入到URL
的查询字符串中,这种情况适用于动态页面之间传值,而不适用于静态页面之间传值; URL
重写需要在服务端上完成,所有的链接都必须带值,因此当一个页面存在很多链接时,其处理过程会是一个不小的挑战;- 对应信息中的某些字符,例如
空格
、单与符号&
和问号?
等必须用base64
编码,而编码和解码会增加开销; URL
的内容会显示在浏览器的地址栏上,所以**URL
重写不适合用来传递敏感信息**。
所以,**URL
重写仅适合于在少量页面间
传递不敏感信息
的情况。**
下面来写个Demo
,使用URL
重写在同一个页面中传递信息。
Demo
项目目录
创件一个名为SessionManagement
的动态Java Web
项目,然后创建项目结果如下所示:
完整代码
1 | package session.management; |
代码详解
URL映射
Servlet
中的URL
映射的注解如下所示:
1 | //配置Servlet映射到URL |
name
表示当前session.management.RewriteURL
这个Servlet
的部署名。urlPatterns
表示该Servlet
的URL
地址,这个是个数字形式,同一个Servlet
可以设置不同的URL
地址.
映射好URL
后,后面就可以通过http://localhost:8080/项目名称/test
这样的格式来访问这个Servlet
,这里是:
http://localhost:8080/SessionManagement/test
初始化数据
1 | private ArrayList<String> fruits = null; |
在这个Servlet中有两个成员变量fruits
和vegetables
用于存放要展示的数据。
当用户第一次通过URL
访问到该Servelet
时,Web
容器(Tomcat
)会调用init()
方法来初始化数据。在这里,init()
方法用来初始化成员变量的数据。
响应get请求 覆盖doGet方法
当直接通过http://localhost:8080/SessionManagement/test这个URL
访问的时候,Tomcat会调用init()
方法来初始化,初始化只进行一次.后面不再调用init()
方法.然后调用doGet()
方法来响应请求。doGet
方法如下:
1 | @Override |
doGet
方法会获取请求中名称为choice
的参数值。然后根据参数值的不同来调用不同的方法进行响应。
当我们请求http://localhost:8080/SessionManagement/test对应的Servlet
时,URL
中没有带查询字符串,所以不存在choice
这个参数。request.getParameter("choice");
返回null.这样就会调用showIndex(response);
这个方法来响应。
返回初始界面
showIndex()
方法如下,所谓的响应,就是往响应对象里写一些字符串。Web
容器会把响应对象中的这些字符串处理一下然后发送给浏览器。
1 | private void showIndex(HttpServletResponse response) |
此时浏览器显示如下:
这个页面有两个超链接,如下所示:
1 | <a href="?choice=fruits">显示水果</a> |
响应重写的URL
在刚才的页面中,显示水果
的URL
为?choice=fruits
这是个查询字符串,查询字符串中有一个名为choice
的参数,参数值为fruits
。
如果点击这超链接,浏览器会再次发送一个HTTP
请求给服务器。
这里URL
中地址缺省,浏览器会默认发给当前的地址。也就是在http://localhost:8080/SessionManagement/test
这个地址后面加上查询字符串,得到的URL为:http://localhost:8080/SessionManagement/test?choice=fruits
,这相当于重写了URL
.
虽然,这个时候请求的Servlet还是http://localhost:8080/SessionManagement/test
这个URL对应的Servlet.但是这回跟第一次请求时的情况是不一样的,第一次没有带请求参数,这次带了请求参数。Tomcat还是会调用doGet来生成响应。
不过这次因为带了参数choice=fruits
,doGet方法会调用showFruits
方法来生成响应。
1 | private void showFruits(HttpServletResponse response) |
这个方法会把水果列表中的数据输出。如下所示:
同理如果此时点击显示蔬菜
链接我们就可以显示蔬菜。
第2章 会话管理
2.2 隐藏域
2.2 隐藏域
使用隐藏域来保持状态类似于URL
重写技术,但不是将值附加到URL
上,而是放到HTML
表单的隐藏域中。当表单提交时,隐藏域的值也同时提交到服务器端。隐藏域技术仅当网页有表单时有效。该技术相对于 URL
重写的优势在于:没有字符数限制,同时无须额外的编码。但该技术同URL
重写一样,不适合跨越多个界面。
清单2.3展示了如何通过隐藏域来更新客户信息。清单2.2的Customer类为客户对象模型。
清单2.2 Customer类
1 | package app02a.hiddenfields; |
清单2.3 CustomerServlet类
1 | package app02a.hiddenfields; |
CustomerServlet
类继承自HttpServlet
,其URL
映射分别为/customer
、/editCustomer
和 /updateCustomer
。前两个URL
会调用Servlet
的doGet
方法, 而/updateCustomer
会调用doPost
方法。/customer
是本例的入口URL
。该URL
会列举出在 init
方法中所初始化的类级别的列表对象customers
(在 真实应用中,通常是从数据库中获取用户信息),如图下图所示。
如图上图所示,每个客户信息后都有一个edit
链接, 每个edit
链接的href
属性为 /editCustomer?id=customerId
。当通过/editCustomer
访问servlet
时, servlet
会返回一个编辑表单,如下图所示。
此时,servlet返回的表单如下:
1 | <form method="post" action="updateCustomer"> |
可以看到表达中的隐藏域为<input type="hidden" name="id" value="1">
.
该隐藏域保存了所编辑的客户id
,因为隐藏域包含在表单中,所以会随着表单一起提交给服务器,这样服务端就知道应更新哪个客户信息。
需要强调的是,表单是通过post
方式提交的,因此调用的是servlet
的doPost
方法。