第2章 Servlet会话跟踪
本章目标
■ 掌握会话跟踪的相关技术
■ 理解Cookie的原理
■ 掌握Cookie的读写方法使用
■ 理解Session的原理
■ 理解Session的生命周期
■ 熟练掌握Session的方法使用
■ 掌握ServletContext的方法使用
学习导航
任务描述
【描述2.D.1】
使用Cookie将用户的登录信息保存到客户端,当用户再次登录时在相应的文本栏中显示上次登录时输入的信息。
【描述2.D.2】
用户通过表单提交数据,使用Session在该用户访问的所有页面中输出此数据。
【描述2.D.3】
基于任务描述2.D.2,通过URL重写来实现Session会话技术。
【描述2.D.4】
使用ServletContext显示在线访问人数。
【描述2.D.5】
编写一个Servlet,访问web.xml中的初始化参数信息。
2.1 会话跟踪简介
HTTP是一种无状态的协议,这就意味着Web服务器并不了解同一用户以前请求的信息,即当浏览器与服务器之间的请求、响应结束后,服务器上不会保留任何客户端的信息。但对于现在的Web应用而言,往往需要记录特定客户端的一系列请求之间的联系,以便于对客户的状态进行追踪。比如,在购物网站,服务器会为每个客户配置一个购物车,购物车需要一直跟随客户,以便于客户将商品放入购物车中,而且每个客户之间的购物车也不会混淆。这就是本章所讲到的会话跟踪技术。
会话跟踪技术的方案包括以下几种:
■ Cookie技术
■ Session技术
■ URL重写技术
■ 隐藏表单域技术
注意 由于隐藏表单域技术是将会话ID添加到隐藏域中,实现起来较为烦琐,因此在实际应用中不推荐使用该技术,本章也不做讲解。
2.2 Cookie
Cookie是服务器发给客户端的一小段文本,保存在浏览器所在客户端的内存或磁盘上。服务器可以从客户端读出这些Cookie。通过Cookie,客户端和服务器端可建立起一种联系,也就是说,Cookie是一种可以让服务器对客户端信息进行保存和获取的机制,从而大大扩展了基于Web的应用功能。
Cookie是会话跟踪的一种解决方案,例如在需要登录的网站,用户第一次输入用户名和密码后,可以将其利用Cookie保存在客户端,当用户下一次访问这个网站的时候,就能直接从客户端读出该用户名和密码来,用户就不需要每次都重新登录。另外,也可以根据需要让用户定制自己喜欢的内容,用户可以选择自己喜欢的新闻、显示的风格、显示的顺序等,这些相关的设置信息都保存到客户端的Cookie中,当用户每次访问该网站时,就可以按照他预设的内容进行显示。
当然,因为Cookie需要将信息保存在客户端的计算机上,所以,从Cookie诞生之日起,有关于它所可能带来的安全问题就一直是人们所关注的焦点。但截至目前,还没有因为Cookie所带来的重大安全问题,这主要也是由Cookie的安全机制所决定的:
■ Cookie不会以任何方式在客户端被执行。
■ 浏览器会限制来自同一个网站的Cookie数目。
■ 单个Cookie的长度是有限制的。
■ 浏览器限制了最多可以接受的Cookie数目。
基于这些安全机制,客户端就不必担心硬盘被这些Cookie占用太大的空间。虽然Cookie不太可能带来安全问题,但可能会带来一些隐私问题,通常情况下,不要将敏感的信息保存到Cookie中,特别是一些重要的个人资料,如信用卡账号、密码等。另外,浏览器可以设置成拒绝Cookie,因此Web开发中不要使程序过度依赖于Cookie,因为一旦用户关闭了浏览器的Cookie功能,就可能造成程序无法正确运行。
2.2.1 Cookie的创建及使用
通过Cookie类的构造方法可以创建该类的实例。Cookie的构造方法带有两个String类型的参数,分别用于指定Cookie的属性名称和属性值,例如:
Cookie userCookie = new Cookie("uName",username);
Cookie类提供了一些方法,常用方法如表2-1所示。
表2-1 Cookie类常用方法
创建完成的Cookie对象,可以使用HttpServletResponse的addCookie()方法将其发送到客户端。addCookie()方法接收一个Cookie类型的值,例如:
// 将userCookie发送到客户端 response.addCookie(userCookie);
使用HttpServletRequest的getCookies()方法可以从客户端获得这个网站的所有Cookie,该方法返回一个包含本站所有Cookie的数组,遍历该数组可以获得对应的Cookie,例如:
Cookie[] cookies = request.getCookies();
默认情况下,Cookie在客户端是保存在内存中的,如果浏览器关闭,Cookie也就失效了。如果想要让Cookie长久地保存在磁盘上,通过使用表2-1中的setMaxAge()方法设置其过期时间,如将客户端的Cookie的过期时间设置为1周,其示例代码如下:
// 在客户端保存一周 userCookie.setMaxAge(7*24*60*60);
注意 Cookie的保存位置在不同的操作系统下是不同的,其中Windows XP系统下其保存位置是在C:\Documents and Settings\当前系统用户名\Local Settings\temporary internet files文件夹中。
2.2.2 Cookie示例
下述代码用于实现任务描述2.D.1,使用Cookie保存用户名和密码,当用户再次登录时在相应的文本框中显示上次登录时输入的信息。(1)编写用于接收用户输入的HTML表单文件,在该例子中,没有使用HTML文件而是使用一个Servlet来完成此功能,这是因为需要通过Servlet去读取客户端的Cookie,而HTML文件无法完成此功能。
【描述2.D.1】 LoginServlet.java
public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String cookieName = "userName"; String cookiePwd = "pwd"; // 获得所有cookie Cookie[] cookies = request.getCookies(); String userName = ""; String pwd = ""; String isChecked = ""; // 如果cookie数组不为null,说明曾经设置过 // 也就是曾经登录过,那么取出上次登录的用户名、密码 if (cookies != null) { // 如果曾经设置过cookie,checkbox状态应该是checked isChecked = "checked"; for (int i = 0; i < cookies.length; i++) { // 取出登录名 if (cookies[i].getName().equals(cookieName)) { userName = cookies[i].getValue(); } // 取出密码 if (cookies[i].getName().equals(cookiePwd)) { pwd = cookies[i].getValue(); } } } response.setContentType("text/html;charset=GBK"); PrintWriter out = response.getWriter(); out.println("<html>\n"); out.println("<head><title>登录</title></head>\n"); out.println("<body>\n"); out.println("<center>\n"); out.println(" <form action='CookieTest'" + " method='post'>\n"); out.println("姓名:<input type='text'" + " name='UserName' value='" + userName + "'><br/>\n"); out.println("密码:<input type='password' name='Pwd' value='" + pwd + "'><br/>\n"); out.println("保存用户名和密码<input type='checkbox'" + "name='SaveCookie' value='Yes' " + isChecked + ">\n"); out.println(" <br/>\n"); out.println(" <input type=\"submit\">\n"); out.println(" </form>\n"); out.println("</center>\n"); out.println("</body>\n"); out.println("</html>\n"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
上述代码中,首先使用request.getCookies()获取客户端cookies数组;再遍历该数组,找到对应的Cookie,取出用户名和密码;最后将信息显示在相应的表单控件中。
(2)编写CookieTest.java程序。
【描述2.D.1】 CookieTest.java
public class CookieTest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie userCookie = new Cookie("userName", request .getParameter("UserName")); Cookie pwdCookie = new Cookie("pwd", request.getParameter("Pwd")); if (request.getParameter("SaveCookie") != null && request.getParameter("SaveCookie").equals("Yes")) { userCookie.setMaxAge(7 * 24 * 60 * 60); pwdCookie.setMaxAge(7 * 24 * 60 * 60); } else { // 删除客户端对应的Cookie userCookie.setMaxAge(0); pwdCookie.setMaxAge(0); } response.addCookie(userCookie); response.addCookie(pwdCookie); PrintWriter out = response.getWriter(); out.println("Welcome," + request.getParameter("UserName")); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
上述代码中,首先创建两个Cookie对象,分别用来储存表单中传递过来的登录名和密码,然后根据客户端的“SaveCookie”元素的值,决定是否向客户端发送Cookie,或者删除以前存储的Cookie。
启动Tomcat,在IE中访问http://localhost:8080/ch02/LoginServlet,运行结果如图2-1所示。
输入姓名和密码,选中相应的保存复选框,单击提交按钮,显示结果如图2-2所示。
图2-1 第一次访问LoginServlet
图2-2 CookieTest结果
当再次登录时,用户名和密码已显示,如图2-3所示。
图2-3 再次访问LoginServlet
2.3 Session
使用Cookie可以将请求的状态信息传递到下一次请求中(如任务描述2.D.1),但是如果传递的状态信息较多,将极大地降低网络传输效率,并且会增大服务器端程序的处理难度,为此各种服务器端技术都提供了一种将会话状态保存在服务器端的方案,即Session(会话)技术。
Session是在Java Servlet API中引入的一个非常重要的机制,用于跟踪客户端的状态,即在一段时间内,单个客户端与Web服务器之间的一连串的交互过程称为一个会话。
HttpSession是Java Servlet API中提供的对Session机制的实现规范,它仅仅是个接口,Servlet容器必须实现这个接口。当一个Session开始时,Servlet容器会创建一个HttpSession对象,并同时在内存中为其开辟一个空间,用来存放此Session对应的状态信息。Servlet容器为每一个HttpSession对象分配一个唯一的标识符,称为SessionID,同时将SessionID发送到客户端,由浏览器负责保存此SessionID。这样,当客户端再发送请求时,浏览器会同时发送SessionID,Servlet容器可以从请求对象中读取SessionID,根据SessionID的值找到相应的HttpSession对象。每个客户端对应于服务器端的一个HttpSession对象,这通过SessionID区分。Session机制如图2-4所示。
图2-4 Session机制
注意 通常服务器借助于Cookie把SessionID存储在浏览器进程中,在该浏览器进程下一次访问服务器时,服务器就可以从请求中的Cookie里获取SessionID。此外,Session还可以借助URL重写的方式在客户端保存SessionID。
2.3.1 Session创建
Servlet容器根据HttpServletRequest对象中提供的SessionID,可以找到对应的HttpSession对象。在HttpServletRequest中提供了以下两种方法来获取HttpSession:
■ getSession()方法取得请求所在的会话,如果该会话对象不存在则创建一个新会话。
■ getSession(boolean create)返回当前请求的会话。如果当前请求不属于任何会话,而且create参数为true,则创建一个会话,否则返回null。此后所有来自同一个对象的请求都属于这个会话,通过它的getSession(false)返回的是当前会话。
例如,可以使用下面两种方式获取当前Session:
HttpSession session=request.getSession(); //获取当前Session
或
HttpSession session=request.getSession(true); //获取当前Session
2.3.2 Session使用
HttpSession中定义了如表2-2所示的方法。
表2-2 HttpSession常用方法
其中,用于存取数据的方法有以下两个。
■ setAttribute():用于在Session对象中保存数据,数据以Key/Value映射形式存放。
■ getAttribute():从Session中提取指定Key对应的Value值。
向Session对象中保存数据的示例代码如下:
// 将username保存到Session中,并指定其引用名称为uName session.setAttribute("uName", username);
从Session中提取存放的信息,则代码如下:
// 取出数据,注意:因该方法的返回数据类型为Object,所以需要转换数据类型 String username = (String)session.getAttribute("uName");
用于销毁Session的方法如下。
■ invalidate():调用此方法可以同时删除HttpSession对象和数据。
使用invalidate()销毁Session的示例代码如下:
// 销毁Session(常用于用户注销) session.invalidate();
注意 有3种情况可以结束Session:
(1)关闭浏览器,Session关闭。
(2)调用HttpSession的invalidate()方法。
(3)两次访问时间间隔大于Session定义的非活动时间间隔。
2.3.3 Session生命周期
Session生命周期经过以下几个过程:
01 客户端向服务器第一次发送请求的时候,request中并无SessionID。
02 此时服务器会创建一个Session对象,并分配一个SessionID。Session对象保存在服务器端,此时为新建状态,调用session.isNew()返回true。
03 当服务器端处理完毕后,会将SessionID通过response对象传回到客户端,浏览器负责保存到当前进程中。
04 当客户端再次发送请求时,会同时将SessionID发送给服务器。
05 服务器根据传递过来的SessionID将这次请求(request)与保存在服务器端的Session对象联系起来。此时Session已不处于新建状态,调用session.isNew()返回false。
06 循环执行过程3~5,直到Session超时或销毁。
Session的生命周期和访问范围如图2-5所示。
图2-5 Session访问范围
在图2-5中,每个客户(如Client1)可以访问多个Servlet,但是一个客户的多个请求将共享一个Session,同一Web应用下的所有Servlet共享一个ServletContext,即Servlet上下文。ServletContext在本章2.5节将详细介绍。
2.3.4 Session演示
下述代码完成任务描述2.D.2,用户在初始化页面中输入一个值,单击“提交”按钮,会进入第一个Servlet;第一个Servlet将输入的值分别保存到request和Session中;在第二个Servlet中从request和Session对象中提取信息并显示。(1)编写session.html页面,代码如下所示。
【描述2.D.2】session.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>Session示例</title> </head> <body> <center> <form method="POST" action="s1"> <table> <tr> <td>输入数据: <input type="text" name="count"></td> </tr> </table> <center><input type="submit" value="提交"></center> </form> </center> </body> </html>
在此页面中,将表单信息提交给第一个名为FirstServlet的Servlet处理。因为第一个Servlet的<url-pattern>是“/s1”,因此表单的action属性值为“s1”。
(2)编写第一个Servlet,代码如下所示。
【描述2.D.2】FirstServlet.java
public class FirstServlet extends HttpServlet { public FirstServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置请求的编码字符为GBK request.setCharacterEncoding("GBK"); // 设置响应的文本类型为html,编码字符为GBK response.setContentType("text/html; charset=GBK"); PrintWriter out = response.getWriter(); // 获取表单数据 String str = request.getParameter("count"); request.setAttribute("request_param", str); HttpSession session = request.getSession(); session.setAttribute("session_param", str); out.println("<a href='s2'>下一页</a>"); } }
上述代码中,首先提取表单数据,并分别保存在request和session对象中。再通过下面语句在客户端浏览器中显示一个超链接,单击此超链接,可以链接到第二个Servlet(第二个Servlet的<url-pattern>设置为“/s2”)。
out.println("<a href='s2'>下一页</a>")
(3)编写第二个Servlet,代码如下所示。
【描述2.D.2】SecondServlet.java
public class SecondServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object obj = request.getAttribute("request_param"); String request_param = null; if (obj != null) { request_param = obj.toString(); } else { request_param = "null"; } HttpSession session = request.getSession(); Object obj2 = session.getAttribute("session_param"); String session_param = null; if (obj2 != null) { session_param = obj2.toString(); } else { session_param = "null"; } response.setContentType("text/html; charset=GBK"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<body >"); out.println("<h2>请求对象中的参数是 :" + request_param + "</h2>"); out.println("<h2>Session对象中的参数是 :" + session_param + "</h2></body></html>"); } }
上述代码中,分别从request和session对象中获取数据并输出。
启动Tomcat,在IE中访问http://localhost:8080/ch02/session.html,运行结果如图2-6所示。
在文本框中输入数据,单击“提交”按钮,提交给FirstServlet处理,运行结果如图2-7所示。
图2-6 session.html
图2-7 FirstServlet运行结果
单击“下一页”超链接,进入SecondServlet,运行结果如图2-8所示。
图2-8 SecondServlet.java运行结果
如图2-8所示,保存在request对象中的数据变为null,而保存在Session对象中的数据是正确的。因为在单击“下一页”超链接后进入第二个Servlet时,上一次的request已经结束,此时是一个新的请求,该request对象中并无保存数据,因此提取的数据只能为null;而这两次请求位于同一个会话中,Session的生命周期并未结束,因此能够获取Session中保存的数据。
在上述任务的执行过程中,服务器在处理客户端的请求时创建了新的HttpSession对象,将会话标识号(SessionID)作为一个Cookie项加入到响应信息中返回给客户端。浏览器再次发送请求时,服务器程序从Cookie中找到SessionID,就可以检索到已经为该客户端创建了的HttpSession对象,而不必再创建新的对象,通过这种方式就实现了对同一个客户端的会话状态跟踪。
注意 使用Cookie实现Session跟踪时,默认情况下Cookie保存在浏览器进程使用的内存中,并没有保存到磁盘上,因此浏览器关闭后,Cookie内容消失,相应的SessionID也就不再存在,所以再次打开浏览器发送请求时,已无法发送上次的SessionID,也就无法在服务器端关联上次的HttpSession对象了。
2.4 URL重写
有时,用户由于某些原因禁止了浏览器的Cookie功能,Servlet规范中还引入了一种补充的会话管理机制,它允许不支持Cookie的浏览器也可以与Web服务器保持连续的会话。这种补充机制要求在需要加入同一会话的每个URL后附加一个特殊参数,其值为会话标识号(SessionID)。当用户单击响应消息中的超链接发出下一次请求时,如果请求消息中没有包含Cookie头字段,Servlet容器则认为浏览器不支持Cookie,它将根据请求URL参数中的SessionID来实施会话跟踪。将SessionID以参数形式附加在URL地址后的技术称为URL重写。
HttpServletResponse接口中定义了两个用于完成URL重写的方法。
■ encodeURL():用于对超链接或Form表单的action属性中设置的URL进行重写。
■ encodeRedirectURL():用于对要传递给HttpServletResponse.sendRedirect()方法的URL进行重写。
注意 encodeURL()和encodeRedirectURL()方法根据请求消息中是否包含Cookie头字段来决定是否进行URL重写。
下述步骤用于实现任务描述2.D.3,基于任务描述2.D.2,通过URL重写来实现Session会话技术。
(1)修改描述2.D.2中FirstServlet.java代码片段,使用encodeURL()方法对下述代码进行重写。
out.println("<a href='s2'>下一页</a>");
改为:
out.println("<a href=" + response.encodeURL("s2") + ">下一页</a>");
(2)禁用IE的Cookie功能,重新启动IE,访问http://localhost:8080/ch02/session.html,查看网页源文件,可以观察到超链接内容如下:
<a href=s2;jsessionid=EE7406414267CAC832A2A08B2FCC1A7E>下一页</a>
通过单击URL重写后的超链接,服务器能够识别同一浏览器发出的请求,从而实现了会话功能。
注意 由于Tomcat发送给浏览器的SessionID的Cookie名称为jsessionid,因此,Tomcat服务器中的URL重写就是在URL中附加了jsessionid参数,其值为SessionID的值。
2.5 ServletContext接口
Servlet上下文是运行Servlet的逻辑容器。同一个上下文中的所有Servlet共享存于其中的信息和属性。在Servlet API中定义了一个ServletContext接口,用于存取Servlet运行的环境或者上下文信息。ServletContext对象可以通过使用ServletConfig对象的getServletContext()方法获得,在Servlet中提供了getServletContext()方法也可以直接获得ServletContext对象。
2.5.1 ServletContext的方法
ServletContext接口中定义了许多有用的方法,如表2-3所示。
表2-3 ServletContext方法列表
其中getAttribute()、setAttribute()、removeAttribute()和getInitParameter()是在Web开发中比较常用的方法,具体的使用方法会在本章后续内容的示例中讲解。
2.5.2 ServletContext的生命周期
ServletContext的生命周期过程如下:
01 新Servlet容器启动的时候,服务器端会创建一个ServletContext对象。
02 在容器运行期间ServletContext对象一直存在。
03 当容器停止时,ServletContext的生命周期结束。
2.5.3 ServletContext示例
下述步骤用于实现任务描述2.D.4,使用Servlet上下文保存访问人数。(1)IndexServlet是所有客户端访问网站时首先需要访问的Servlet。每当有一个客户访问该Servlet时,人数将加1,并且保存到Servlet上下文中,这样,在此应用中的任何程序都可以访问到该计数器的值。
【描述2.D.3】IndexServlet.java
public class IndexServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext ctx = this.getServletContext(); synchronized (this) { Integer counter = (Integer) ctx.getAttribute("UserNumber"); int tmp = 0; // 如果counter为null, // 说明servlet上下文中还没有设置UserNumber属性 // 此次访问为第一次访问 if (counter == null) { counter = new Integer(1); } else { // 取出原来计数器的值加上1 tmp = counter.intValue() + 1; counter = new Integer(tmp); } ctx.setAttribute("UserNumber", counter); } response.setContentType("text/html;charset=GBK"); PrintWriter out = response.getWriter(); out.println("<HTML>"); out.println("<HEAD><TITLE>首页</TITLE></HEAD>"); out.println("<BODY>"); out.println("这是第一页<BR>"); out.println("<a href='UserNumber'>人数统计</a>"); out.println("</BODY></HTML>"); } }
在上述代码中,首先通过HttpServlet的getServletContext()方法获得对应的ServletContext对象。然后,通过ServletContext的getAttribute()方法读取名为“UserNumber”的属性值,如果这个属性值不存在(返回值null),说明“UserNumber”还没有被设置,此次访问为第一次访问,否则,将其中的计数值读取出来加上1,再写回到上下文中。另外,为了防止多个客户同时访问这个Servlet引起数据不同步问题,此处使用synchronized进行了同步控制。
(2)在另一个Servlet程序UserNumber.java中读取保存在Servlet上下文中的数据。
【描述2.D.3】UserNumber.java
public class UserNumber extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext ctx = this.getServletContext(); Integer counter = (Integer) ctx.getAttribute("UserNumber"); response.setContentType("text/html;charset=GBK"); PrintWriter out = response.getWriter(); out.println("<HTML>"); out.println("<HEAD><TITLE>访问人数统计</TITLE></HEAD>"); out.println("<BODY>"); if (counter != null) { out.println("已经有" + counter.intValue() + "人次访问本网站!"); } else { out.println("你是第一个访问本网站的!"); } out.println("</BODY></HTML>"); } }
在上述代码中,也是先通过HttpServlet的getServletContext()方法获得对应的Servlet上下文对象;然后通过ServletContext对象的getAttribute()方法来获得计数器的值,并且将它输出到客户端。
启动Tomcat,在IE中访问http://localhost:8080/ch02/IndexServlet,运行结果如图2-9所示。
单击“人数统计”超链接,查看人数,运行结果如图2-10所示。
再新打开两个IE进程窗口,进行访问,运行结果如图2-11所示。
图2-9 访问IndexServlet
图2-10 访问统计
图2-11 访问统计
2.5.4 初始化参数和ServletConfig
ServletContext中除了存取和Web应用全局相关的属性外,还可以通过getInitParameter()方法获得设置在web.xml中的初始化参数。
下述代码用于实现任务描述2.D.4,访问web.xml中的初始化参数信息。
(1)在web.xml中设置参数信息。
【描述2.D.4】在web.xml中配置初始化参数
<web-app> <!-- 初始化参数 --> <context-param> <!-- 参数名 --> <param-name>serverName</param-name> <!-- 参数值 --> <param-value>localhost</param-value> </context-param> <context-param> <param-name>dbInstance</param-name> <param-value>nitpro</param-value> </context-param> <context-param> <param-name>userName</param-name> <param-value>system</param-value> </context-param> <context-param> <param-name>userPwd</param-name> <param-value>manager</param-value> </context-param> <!--其他配置--> </web-app>
在该web.xml中,设置了4个全局初始化参数,它们的名字为“serverName”、“dbInstance”、“userName”和“userPwd”。
(2)在Servlet中访问web.xml初始化参数,并输出。
【描述2.D.4】InitParamServlet.java
public class InitParamServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=GBK"); PrintWriter out = response.getWriter(); // 获得ServletContext对象 ServletContext ctx = this.getServletContext(); // 获得web.xml中设置的初始化参数 String serverName = ctx.getInitParameter("serverName"); String dbInstance = ctx.getInitParameter("dbInstance"); String userName = ctx.getInitParameter("userName"); String password = ctx.getInitParameter("userPwd"); out.println("<HTML>"); out.println("<HEAD><TITLE>"); out.println("读取初始化参数</TITLE></HEAD>"); out.println("<BODY>"); out.println("服务器:" + serverName + "<br>"); out.println("数据库实例:" + dbInstance + "<br>"); out.println("用户名称:" + userName + "<br>"); out.println("用户密码:" + password + "<br>"); out.println("</BODY></HTML>"); } }
在上述代码中,通过使用ServletContext对象的getInitParameter()方法获得在web.xml中设置的初始化参数。
启动Tomcat,在IE中访问http://localhost:8080/ch02/InitParamServlet,运行结果如图2-12所示。
图2-12 读取初始化参数
初始化参数也可以通过使用ServletConfig对象中的getInitParameter()方法来获得,例如:
ServletConfig sc = getServletConfig(); name = sc.getInitParameter("userName ");
注意 Servlet容器初始化一个Servlet对象时,会为该Servlet对象分配一个ServletConfig对象。ServletConfig对象包含Servlet的初始化参数信息,它与ServletContext关联。ServletConfig和ServletContext主要区别如下:
(1)ServletConfig作用于某个特定的Servlet。
(2)ServletContext作用于整个Web应用,是所有Servlet的上下文环境。
小结
通过本章的学习,学生应该能够学会:
■ Cookie是保存在客户端的小段文本。
■ 通过请求可以获得Cookie,通过响应可以写入Cookie。
■ Session是浏览器与服务器之间的一次通话,它包含浏览器与服务器之间的多次请求、响应过程。
■ Session可以在用户访问一个Web站点的多个页面时共享信息。
■ 在Servlet中通过request.getSession()获取当前Session对象。
■ 关闭浏览器、调用Session的invalidate()方法或者等待Session超时都可以使Session失效。
■ HttpSession使用getAttribute()和setAttribute()方法读写数据。
■ ServletContext是运行Servlet的容器。
■ 在Servlet中可通过getServletContext()方法获取ServletContext实例。
■ ServletContext使用getAttribute()和setAttribute()方法读写数据。
练习
1. 下列关于Cookie的说法正确的是______。(多选)
A. Cookie保存在客户端
B. Cookie可以被服务器端程序修改
C. Cookie中可以保存任意长度的文本
D. 浏览器可以关闭Cookie功能
2. 写入和读取Cookie的代码分别是______。
A. request.addCookies()和response.getCookies()
B. response.addCookie()和request.getCookie()
C. response.addCookies()和request.getCookies()
D. response.addCookie()和request.getCookies()
3. Tomcat的默认端口号是______。
A. 80
B. 8080
C. 8088
D. 8000
4. HttpServletRequest的______方法可以得到会话。(多选)
A. getSession()
B. getSession(boolean)
C. getRequestSession()
D. getHttpSession()
5. 下列选项中可以关闭会话的是______。(多选)
A. 调用HttpSession的close()方法
B. 调用HttpSession的invalidate()方法
C. 等待HttpSession超时
D. 调用HttpServletRequest的getSession(false)方法
6. 在HttpSession中写入和读取数据的方法是______。
A. setParameter()和getParameter()
B. setAttribute()和getAttribute()
C. addAttribute()和getAttribute()
D. set()和get()
7. 下列关于ServletContext的说法正确的是______。(多选)
A. 一个应用对应一个ServletContext
B. ServletContext的范围比Session的范围要大
C. 第一个会话在ServletContext中保存了数据,第二个会话读取不到这些数据
D. ServletContext使用setAttribute()和getAttribute()方法操作数据
8. 关于HttpSession的getAttribute()和setAttribute()方法,正确的说法是______。(多选)
A. getAttribute()方法返回类型是String
B. getAttribute()方法返回类型是Object
C. setAttribute()方法保存数据时如果名字重复会抛出异常
D. setAttribute()方法保存数据时如果名字重复会覆盖以前的数据
9. 使HttpSession失效的三种方式是______、______和______。
10. 测试在其他浏览器下Session的生命周期,如Firefox、Chrome等。