一、ServletContext接口
javax.servlet.ServletContext
1、得到servletcontext引用
//方法一:直接调用getServletContext()
ServletContext context=getServletContext();
//方法二:先得到ServletContext引用后,在调用它的getServletContext()
ServletContext context=getServletConfig().getServletContext();
2、获取应用程序的初始化参数
检索Servlet上下文初始化参数的两个方法:
public String getInitParameter(String name);//返回指定参数名的字符串参数值,如果参数不存在返回null
public Enumeration getInitParameterNames();//返回一个包含所有初始化参数名称的Enumeration对象
3、通过ServletContext对象获得资源
使用这些方法可以访问任何资源而不必关心资源所处的位置
public URL getResource(String path);//返回由给定路径指定的资源的URL对象。路径必须以“/”开头,相对于Web应用程序的文档根目录
public InputStream getResourceAsStream(String path);//等价于getResource(path).openStream()从资源获得一个InputStream对象
public String getRealPath(String path);//返回给定的相对路径的绝对路径
例子:
package com.dom;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/filedownload")
public class fileDownloadServlet extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置文件的内容类型
response.setContentType("image/jpg");
//设置content-Disposition响应头,指定文件名
response.setHeader("Content-Disposition","attachment;filename=duke.jpg");
//获得输出流对象
OutputStream os=response.getOutputStream();
ServletContext context=getServletContext();
//返回输入流对象
InputStream is=context.getResourceAsStream("/imgs/img3.jpg");
byte[] bytearray=new byte[1024];
int bytesread=0;
//从输入流中读取1KB,然后写到输出流中
while((bytesread=is.read(bytearray))!=-1){
//将数据发送到客户端
os.write(bytearray, 0, bytesread);
}
os.flush();
is.close();
}
}
图片文档位置
运行结果:
4、登录日志
使用ServletContext接口的log()可以将指定的信息写到服务器的日志文件中,该方法有两种格式:
public void log(String msg);//参数msg为写到日志文件中的消息
public void log(String msg,Throwable throwable);//将msg指定的消息和异常的栈跟踪信息写入日志文件
5、使用RequestDispatcher实现请求转发
RequestDispatcher getRequestDispatcher(String path);//参数path表示资源路径,必须以“/”开头,表示相对于Web应用的文档根目录。如果不能返回一个RequestDispatcher对象,该方法将返回null
RequestDispatcher getNamedDispatcher(String name);//参数name为一个命名的Servlet对象。Servlet和JSP页面都可以通过DD文件指定名称
6、使用ServletContext对象存储数据
使用ServletContext对象也可以存储数据,该对象也是一个作用域对象,作用域是整个应用程序。
处理属性的方法
public void setAttribute(String name,Object object);//将给定名称的属性值对象绑定到上下文对象上。
public Object getAttribute(String name);//返回绑定到上下文对象上的给定名称的属性值,如果没有该属性,返回null
public Enumeration getAttributeNames();//返回绑定到上下文对象上的所有属性名的Enumeration对象
public void removeAttribute(String name);//从上下文对象中删除指定名称的属性
7、检索Servlet容器的信息
getServerInfo()//返回Servlet所运行容器的名称和版本
getMajorVersion()//返回容器所支持的Servlet API的主版本号
getMinorVersion()//返回容器所支持的Servlet API的次版本号
getServletContextName()//返回与该ServletContext对应的Web应用程序名称,它是在web.xml中使用<display-name>元素定义的名称
二、会话管理
1、理解状态与会话
协议记住用户及其请求的能力称为状态(state)。按这个观点,协议分成两种类型:有状态的和无状态的
HTTP的无状态特性
HTTP是一种无状态的协议,HTTP服务器对客户的每个请求和响应都是作为一个分离的事务处理。服务器无法确定多个请求是来自相同的客户还是不同的客户,这意味着服务器不能在多个请求中维护客户的状态。
会话的概念
会话(session)是一个客户与服务器之间的不间断的请求响应序列。当一个客户向服务器发送第一个请求时就开始了一个会话。对该客户之后的每个请求,服务器能够识别出请求来自同一个客户。当客户明确结束会话或服务器在一个预定义的时限内没从客户接受任何请求时,会话就结束了。当会话结束后,服务器就忘记了客户以及客户的请求。
2、会话管理机制
容器通过javax.servlet.http.HttpSession接口抽象会话的概念。
容器使用HttpSession对象管理会话的过程
1、当客户向服务器发送第一个请求时,服务器就可以为该客户创建一个HttpSession会话对象,并将请求对象与该会话对象关联。服务器在创建会话对象时为其指定一个唯一标识符,称为会话ID,它可作为该客户的唯一标识。此时,该会话处于新建状态,可以使用HttpSession接口的isNew()来确定会话是否属于该状态。
2、当服务器向客户发送响应时,服务器将该会话ID与响应数据一起发送给客户,这是通过Set-Cookie响应头实现的,响应消息例如:
HTTP/1.1/ 200 OK
Set-Cookie:JSESSIONID=2387378C687AFD8C892AC845ACF678A3
Content-Type:text/html
..............
这里JSESSIONID的值即为会话ID,它是32位的十六进制数
3、客户在接收到响应后将会话ID存储在浏览器的内存中。当客户再次向服务器发送一个请求时,它将通过Cookie请求头把会话ID与请求一起发送给服务器。例如
POST/helloweb/selectProduct.do HTTP/1.1
Host:www.mydomain.com
Cookie:JSESSIONID=2387378C687AFD8C892AC845ACF678A3
..................
4、服务器接受到请求后,从请求对象中取出会话ID,在服务器中查找之前创建的会话对象,找到后将该请求与之前创建的ID值相同的会话对象关联起来。
上述过程的第(2)~(4)步一直保持重复。如果客户在指定时间没有发送任何请求,服务器将使会话对象失效。一旦会话对象失效,即使客户的请求被认为是第一次请求(如第一步),它不与某个存在的会话对象关联。服务器可以为客户创建一个新的会话对象。
*不能使用客户的IP地址唯一标识客户。因为,客户可能是通过局域网访问Internet.尽管在局域网中每个客户有一个IP地址,但对于服务器来说,客户的实际IP地址是路由器的IP地址,所以该局域网的所有客户的IP地址都相同,因此也就无法唯一标识客户。
3、HttpSession API
1、public String getId()//返回为该会话指定的唯一标识符,它是一个32位的十六进制数
2、public long getCreationTime()//返回会话创建的时间。时间为从1970年1月1日午夜到现在的毫秒数
3、public long getLastAccessedTime()//返回会话最后被访问的时间
4、public boolean isNew()//如果会话对象还没有同客户关联,则返回true
5、public ServletContext getServletContext();//返回该会话所属的ServletContext对象
6、public void setAttribute(String name,Object value)//将一个指定名称和值的属性存储到会话对象上
7、public Object getAttribute(String name)//返回存储到会话上的指定名称的属性值。如果没有指定名称的属性,则返回null
8、public Enumeration getAttributeNames()//返回存储在会话上的所有属性名的一个枚举对象
9、public void removeAttribute(String name)//从会话中删除存储的指定名称的值
10、public void setMaxInactiveInterval(int interval)//设置在容器使该会话失效前客户的两个请求之间最大间隔的时间,单位为秒。参数为负值表示会话永不失效
11、public int getMaxInactiveInterval()//返回以秒为单位的最大间隔时间,在这段时间内,容器将在客户请求之间保持该会话打开状态
12、public void invalidate()//使会话对象失效并删除存储在其上的任何对象
4、使用HttpSession对象
使用HttpSession对象通常需要三步:
1、创建或返回与客户请求关联的会话对象
2、在会话对象中添加或删除“名/值”对象
3、如果需要可使会话失效
创建或返回HttpSession对象需要使用HttpServletRequest接口提供的getSession()
public HttpSession getSession(boolean create)//返回或创建与当前请求关联的会话对象。如果没有与当前请求关联的会话对象,当参数为true时创建一个新的会话对象,当参数为false时,返回null
public HttpSession getSession()//该方法与调用getSession(true)等价
例子
package com.session;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/ShowSessionServlet.do")
public class ShowSessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//创建或返回用户会话对象
HttpSession session=request.getSession(true);
String heading=null;
//从会话对象中检索accessCount属性
Integer accessCount=(Integer)session.getAttribute("accessCount");
if(accessCount==null){
accessCount=new Integer(1);
heading="欢迎您,首次登陆该页面";
}else{
heading="欢迎您,再次登陆该页面";
accessCount+=1;
}
//将accessCount作为属性存储到会话对象中
session.setAttribute("accessCount",accessCount);
PrintWriter out=response.getWriter();
out.println("<html><head>");
out.println("<title>会话跟踪实例</title></head>");
out.println("<body><center>");
out.println("<h4>"+heading+"<a href='ShowSessionServlet.do'>再次访问</a></h4>");
out.println("<table border='0'>");
out.println("<tr bgcolor='#ccc'><td>信息</td><td>值</td><tr/>");
String state=session.isNew()?"新会话":"旧会话";
out.println("<tr><td>会话状态</td><td>"+state+"</td></tr>");
out.println("<tr><td>会话ID</td><td>"+session.getId()+"</td></tr>");
out.println("<tr><td>创建时间</td><td>"+new Date(session.getCreationTime())+"</td></tr>");
out.println("<tr><td>最近访问时间</td><td>"+new Date(session.getLastAccessedTime())+"</td></tr>");
out.println("<tr><td>最大不活动时间</td><td>"+session.getMaxInactiveInterval()+"</td></tr>");
out.println("<tr><td>Cookie</td><td>"+request.getHeader("Cookie")+"</td></tr>");
out.println("<tr><td>Set-Cookie</td><td>"+request.getHeader("Set-Cookie")+"</td></tr>");
out.println("<tr><td>已被访问的次数</td><td>"+accessCount+"</td></tr>");
out.println("</table></center></body></html>");
}
}
运行结果:
5、会话超时与失效
可以在DD文件中设置会话超时时间
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<session-timeout>元素中指定以分钟为单位的超时期限,默认下30分钟。0或小于0的值表示会话永不过期。如果用户在指定期限内没有执行任何动作,服务器就认为用户处于不活动状态并使会话对象失效。
例子:
package com.session.timeout;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/*
* 当使用GET请求访问它时,生成一个0~100之间的随机整数,将其作为一个属性存储到用户的会话对象中,同时提供一个表单供用户输入猜测的数。
* 如果该Servlet接收到一个POST请求,它将比较用户猜到的数和随机生成的数是否相等。若相等在响应页面中给出信息,否则,应该告诉用户猜的数
* 是大还是小,并允许用户重新猜
*/
@WebServlet("/GuessNumberServlet")
public class GuessNumberServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int magic=(int)(Math.random()*101);
HttpSession session=request.getSession();
//session.setAttribute(String,Object);
session.setAttribute("num", new Integer(magic));
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println("<html><head>");
out.println("<title>猜数字</title>");
out.println("</head><body>");
out.println("我想出一个0到100之间的数,请你猜!");
out.println("<form action='GuessNumberServlet' method='post'>");
out.println("<input type='text' style='width:300px' name='guess'>");
out.println("<input type='submit' value='确定'/>");
out.println("</form></body></html>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int guess=Integer.parseInt(request.getParameter("guess"));
HttpSession session=request.getSession();
int magic=(Integer)session.getAttribute("num");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=response.getWriter();
out.println("<html><body>");
if(guess==magic){
session.invalidate();//销毁会话对象
out.println("祝贺你,答对了");
out.println("<a href='/ServletContext/GuessNumberServlet'>再来一次</a>");
}
else{
if(guess>magic){
out.println("太大了,请重猜!");
}else{
out.println("太小了,请重猜!");
}
out.println("<form action='GuessNumberServlet' method='post'>");
out.println("<input type='text' name='guess' style='width:300px'/>");
out.println("<input type='submit' value='确定'/>");
out.println("</body></html>");
}
}
}
二、Cookie及其应用
1、Cookie API
javax.servlet.http.Cookie
public Cookie(String name,String value)
Cookie类的常用方法
1、public String getName()//返回Cookie名称,名称一旦创建不能改变
2、public String getValue()//返回Cookie的值
3、public void setValue(String new Value)//在Cookie创建后为它指定一个新值
4、public void setMaxAge(int expiry)//设置Cookie在浏览器中的最长存活时间,单位秒。负值表示不永久存储,0表示删除该Cookie
5、public void getMaxAge()//返回Cookie在浏览器上的最大存活时间
6、public void setDomain(String pattern)//设置该Cookie所在的域。域名以点号(.)开头,例如:.foo.com。默认情况下,只有发送Cookie的服务器才能得到它。
7、public String getDomain()//返回为该Cookie设置的域名
2、向客户端发送Cookie
要把Cookie发送到客户端,Servlet先使用Cookie类的构造方法创建一个Cookie对象,通过setXxx()设置各种属性,通过响应对象的addCookie(cookie)把Cookie加入响应头。
1、创建Cookie对象
Cookie userCookie=new Cookie("username","hacker");
2、设置Cookie的最大存活时间
userCookie.setMaxAge(60*60);//一小时
在默认情况下,发送到客户端的Cookie对象只是一个会话级别的Cookie,它存储在浏览器的内存中,用户关闭浏览器后Cookie对象将被删除。如果希望浏览器将Cookie对象存储磁盘上,需要使用Cookie类的setMaxAge()设置Cookie的最大存活时间。
3、向客户发送Cookie对象
要将Cookie对象发送到客户端,需要调用响应对象的addCookie()将Cookie添加到Set-Cookie响应头
response.addCookie(userCookie);
3、从客户端读取Cookie
需从客户端读入Cookie,Servlet应该调用请求对象的getCookies(),该方法返回一个Cookie对象的数组。大多数情况下,只需循环访问数组的各个元素寻找指定名字的Cookie,然后对该Cookie调用getValue()取得与指定名字关联的值。
步骤:
1、调用请求对象的getCookies方法
该方法返回一个Cookie对象的数组。如果请求中不含Cookie,返回null值
Cookie[] cookies=request.getCookies();
2、对Cookie数组循环
4、Cookie的安全问题
浏览器 一般只允许存放300个Cookie,每个站点的Cookie最多存放20个,每个Cookie的大小限制为4KB,因此Cookie不会占据硬盘太多空间。
5、例子
自动登录功能
以GET方法访问CheckUserServlet(在浏览器地址栏中输入该Servlet的URL地址),由于是首次访问,请求中并不包含Cookie,该Servlet将响应重定向到MyJsp.jsp页面。在该页面中如果用户输入了正确的用户名和口令,并勾选了“自动登录”复选框,单击“提交”按钮,将发送POST请求由CheckUserServlet的doPost()处理。在该方法中使用用户名和口令创建两个Cookie对象并发送到客户端。
之后在发送GET请求,Servlet将从Cookie中检索出用户名和口令,并对其验证。验证通过后将响应重定向到Welcome.jsp页面
CheckUserServlet.java
package com.MyCookieTest;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/checkUserServlet")
public class CheckUserServlet extends HttpServlet {
String message=null;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
System.out.println("1");
String value1="value1";
String value2="value2";
Cookie cookie=null;
Cookie[]cookies=request.getCookies();
if(cookies!=null){
for(int i=0;i<cookies.length;i++){
cookie=cookies[i];
if(cookie.getName().equals("username")){
value1=cookie.getValue();
}
if(cookie.getName().equals("password")){
value2=cookie.getValue();
}
}
if(value1.equals("admin")&&value2.equals("admin")){
message="欢迎您"+value1+"再次登录该页面!";
request.getSession().setAttribute("message",message);
response.sendRedirect("Welcome.jsp");
}else{
response.sendRedirect("MyJsp.jsp");
}
System.out.println(request.getHeader("Cookie"));
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("2");
message=null;
response.setContentType("text/html;charset=utf-8");
String username=request.getParameter("username");
String password=request.getParameter("password");
if(!username.equals("admin")){
if(!password.equals("admin")){
message="用户名与口令不匹配,请重试";
request.getSession().setAttribute("message", message);
response.sendRedirect("http://localhost:8080/MyCookie/MyJsp.jsp");
}
}else{
//如果用户选中了“自动登录”复选框,向浏览器发送两个Cookie
if(request.getParameter("check")!=null&&request.getParameter("check").equals("check")){
Cookie nameCookie=new Cookie("username",username);
Cookie passCookie=new Cookie("password",password);
nameCookie.setMaxAge(60*60);
passCookie.setMaxAge(60*60);
response.addCookie(nameCookie);
response.addCookie(passCookie);
}
message=username+"已经成功登录";
request.getSession().setAttribute("message",message);
System.out.println(request.getHeader("Cookie"));
response.sendRedirect("http://localhost:8080/MyCookie/Welcome.jsp");
}
}
}
MyJsp.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录页面</title>
</head>
<body>
${sessionScope.message}<br>
<form action="checkUserServlet" method="post">
请输入用户名和口令:<br>
用户名:<input type="text" name="username"/><br/>
口令:<input type="password" name="password"/><br/>
<input type="checkbox" name="check" value="check"/>自动登录<br>
<input type="submit" value="提交"/>
<input type="reset" value="重置"/>
</form>
</body>
</html>
Welcome.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
This is my JSP page.
</body>
</html>
测试结果: