#### 学习内容
1、状态管理
2、Cookie的使用
3、Cookie的路径
4、Session的工作原理
5、Session的使用
6、URL重定向
7、ServletContext(application)
#### 目标
1、掌握状态管理
2、掌握Cookie的使用
3、掌握Cookie的路径
4、掌握Session的工作原理
5、掌握Session的使用
6、掌握URL重定向
7、掌握ServletContext(application)
## 第一节 Servlet生命周期
##### 1 什么是生命周期
生命周期也就是生命历程,就像人都是从怀胎–>出生–>婴儿–>儿童–>少年–>成年–>中年–>老年–>死亡
###### 1.1 生命周期的四个阶段
阶段一、实例化(调用构造方法)
实例化阶段是Servlet生命周期中的第一步,由Servlet容器调用Servlet的构造器创建一个具体的Servlet对象的过程。而这个创建的时机可以是在容器收到针对这个组件的请求之后,即用了才创建;也可以在容器启动之后立刻创建实例,而不管此时Servlet是否使用的上。使用如下代码可以设置Servlet是否在服务器启动时就执行创建
1 //影响创建的时机
阶段二、初始化(init方法)
Servlet在被加载实例化之后,必须要初始化它。在初始化阶段,init()方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个ServletConfig类型的对象作为参数。ServletConfig对象由Servlet引擎负责创建,从中可以读取到事先在web.xml文件中通过节点配置的多个name-value名值对。ServletConfig对象还可以让Servlet接受一个ServletContext对象。
一般情况下,init方法不需要编写,因GenericServlet已经提供了init方法的实现,并且提供了getServletConfig方法来获得ServletConfig对象。
注:init方法只被执行一次
阶段三、就绪/服务 service ---->HTTPServlet
Servlet被初始化以后就处于能够响应请求的就绪状态。每个对Servlet的请求由一个ServletRequest对象代表,Servlet给客户端的响应由一个ServletResponse对象代表。当客户端有一个请求时,容器就会将请求与响应对象转给Servlet,以参数的形式传给service方法。service方法由javax.servlet.Servlet定义,由具体的Servlet实现
HttpServlet将service方法拆分了。doGet和doPost
阶段四、销毁
Servlet容器在销毁Servlet对象时会调用destroy方法来释放资源。通常情况下Servlet容器停止或者重新启动都会引起销毁Servlet对象的动作,但除此之外,Servlet容器也有自身管理Servlet对象的准则,整个生命周期并不需要人为进行干预
Servlet代码如下:
```java
/**
* Servlet implementation class LifeServlet
* 演示Servlet的生命周期:
* 1、实例化
* 2、init:初始化
* 3、service:服务
* 4、destory:销毁
*/
@WebServlet("/LifeServlet")
public class LifeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public LifeServlet() {
super();
System.out.println("1、完成了实例化");
// TODO Auto-generated constructor stub
}
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
System.out.println("2、完成了初始化");
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("3、就绪中");
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
super.destroy();
System.out.println("4、销毁了");
}
}
因为每次请求都会创建一个线程,如果多人同时请求,那么就会存在多个线程操作同一个Servlet对象,那么如果在对应的方法中操作了成员变量,就有可能产生线程安全的问题。
1、synchronized
将存在线程安全问题的代码放到同步代码块中
2、实现SingleThreadModel接口
servlet实现singleThreadModel接口后,每个线程都会创建servlet实例,这样每个客户端请求就不存在共享资源的问题,但是servlet响应客户端请求的效率太低,所以已经淘汰。
3、尽可能只使用局部变量
name
张三
1 init-param元素用来定义Servlet启动的参数,可以定义多个
2 param-name表示参数名称
3 param-value表示参数值
initParams:配置Servlet的初始化参数
initParams ={@WebInitParam(name = "username",value = "aqiang"),@WebInitParam(name = "password",value = "123")}
HTTP协议是无状态的,不能保存每次提交的信息,即当服务器返回与请求相对应的应答之后,这次事务的所有信息就丢掉了。
如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。
对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。
WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
WEB应用的会话状态是指WEB服务器与浏览器在会话过程中产生的状态信息,借助会话状态,WEB服务器能够把属于同一会话中的一系列的请求和响应过程关联起来。
数据共享
客户端状态管理技术:将状态保存在客户端。代表性的是Cookie技术。
服务器状态管理技术:将状态保存在服务器端。代表性的是session技术(服务器传递sessionID时需要使用Cookie的方式)。
Cookie是在浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头中附带传送给浏览器的一小段数据,WEB服务器传送给各个客户端浏览器的数据是可以各不相同的。
一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都应在HTTP请求头中将这个Cookie回传给WEB服务器。
WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给WEB服务器。
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB
//创建Cookie
Cookie ck=new Cookie("code", code);
ck.setPath("/");//设置Cookie的路径
ck.setMaxAge(-1);//内存存储,取值有三种:>0有效期,单位秒;=0失效;<0内存存储,默认-1
response.addCookie(ck);//让浏览器添加Cookie
chrome浏览器查看cookie信息:chrome://settings/content/cookies
//获取所有的Cookie
Cookie[] cks=request.getCookies();
//遍历Cookie
for(Cookie ck:cks){
//检索出自己的Cookie
if(ck.getName().equals("code"))
{
//记录Cookie的值
code=ck.getValue();
break;
}
}
只需要保证Cookie的名和路径一致即可修改
//创建Cookie
Cookie ck=new Cookie("code", code);
ck.setPath("/");//设置Cookie的路径
ck.setMaxAge(-1);//内存存储,取值有三种:>0有效期,单位秒;=0失效;<0内存存储
response.addCookie(ck);//让浏览器添加Cookie
ck.setMaxAge(-1);设置生成时间,默认-1
取值说明:
>0有效期,单位秒
=0失效
<0内存存储
中文和英文字符不同,中文属于Unicode字符,在内存中占用4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会出现乱码。编码可以使用java.net.URLEncoder类的encode(String str,String encoding)方法,解码使用java.net.URLDecoder类的decode(String str,String encoding)方法
代码如下:
保存:Servlet类
// 使用中文的 Cookie. name 与 value 都使用 UTF-8 编码.
Cookie cookie = new Cookie(
URLEncoder.encode("姓名", "UTF-8"),
URLEncoder.encode("老邢", "UTF-8"));
// 发送到客户端
response.addCookie(cookie);
读取:Servlet读取
if(request.getCookies() != null){
for(Cookie cc : request.getCookies()){
String cookieName = URLDecoder.decode(cc.getName(), "UTF-8");
String cookieValue = URLDecoder.decode(cc.getValue(), "UTF-8");
out.println(cookieName + "=");
out.println(cookieValue + ";
");
}
}
else{
out.println("Cookie 已经写入客户端. 请刷新页面. ");
}
cookie 一般都是由于用户访问页面而被创建的,可是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。
cookie.setPath("/");
浏览器在发送请求之前,首先会根据请求url中的域名在cookie列表中找所有与当前域名一样的cookie,然后再根据指定的路径进行匹配,如果当前请求在域匹配的基础上还与路径匹配那么就会将所有匹配的cookie发送给服务器。
通过Cookie的setPath方法设置路径
优点:
可配置到期规则:Cookie 可以在浏览器会话结束时到期,或者可以在客户端计算机上无限期存在,这取决于客户端的到期规则,不需要任何服务器资源,Cookie 存储在客户端并在发送后由服务器读取。
简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
数据持久性:虽然客户端计算机上 Cookie 的持续时间取决于客户端上的 Cookie 过期处理和用户干预,Cookie 通常是客户端上持续时间最长的数据保留形式
缺点:
大小受到限制:大多数浏览器对 Cookie 的大小有 4096 字节的限制,尽管在当今新的浏览器和客户端设备版本中,支持 8192 字节的 Cookie 大小已愈发常见。
用户配置为禁用:有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。
潜在的安全风险:Cookie 可能会被篡改。用户可能会操纵其计算机上的 Cookie,这意味着会对安全性造成潜在风险或者导致依赖于Cookie 的应用程序失败。
Session用于跟踪客户端的状态。Session指的是在一段时间内,单个客户与Web服务器的一连串相关的交互过程。
在一个Session中,客户可能会多次请求访问同一个网页,也有可能请求访问各种不同的服务器资源。
session被用于表示一个持续的连接状态,在网站访问中一般指代客户端浏览器的进程从开启到结束的过程。session其实就是网站分析的访问(visits)度量,表示一个访问的过程。
session的常见实现形式是cookie(session cookie),即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则Tomcat会创造一个名为JSESSIONID的输出 cookie返回给浏览器(只放入内存,并不存在硬盘中),并将其以HashTable的形式写到服务器的内存里面;当已经包含sessionid时,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。这里需要注意的是session始终是有服务端创建的,并非浏览器自己生成的。
//获取Session对象
HttpSession session=request.getSession();
System.out.println("Id:"+session.getId());//唯一标记,
System.out.println("getLastAccessedTime:"+session.getLastAccessedTime());//最后一次访问时间,毫秒
System.out.println("getMaxInactiveInterval:"+session.getMaxInactiveInterval());//获取最大的空闲时间,单位秒
System.out.println("getCreationTime:"+session.getCreationTime());//获取Session的创建,单位毫秒
使用HttpSession的setAttribute(属性名,Object)方法
使用HttpSession的invalidate方法
HttpSession的最后一次访问时间和当前时间的差距大于了指定的最大空闲时间,这时服务器就会销毁Session对象。默认的空闲时间为30分钟。
1 使用HttpSession的session.setMaxInactiveInterval(20*60);设置,单位秒
2 在web.xml中配置 ,单位分钟
20
不冲突
1、超过了设置的超时时间
2、主动调用了invalidate方法
3、服务器主动或异常关闭
注意:浏览器关闭并不会让Session失效
如果浏览器禁用Cookie,session还能用吗?
答:不能,但有其他的解决方案
服务器在默认情况下,会使用Cookie的方式将sessionID发送给浏览器,如果用户禁止Cookie,则sessionID不会被浏览器保存,此时,服务器可以使用如URL重写这样的方式来发送sessionID.
使用Session区分每个用户的方式:
1、使用Cookie
2、作为隐藏域嵌入HTML表单中,附加在主体的URL中,通常作为指向其他应用程序页面的链接,即URL重写。
浏览器在访问服务器上的某个地址时,不再使用原来的那个地址,而是使用经过改写的地址(即,在原来的地址后面加上了sessionID)
如果是链接地址和表单提交,使用
response.encodeURL(String url)生成重写后的URL
如果是重定向,使用
response.encodeRedirectURL(String url)生成重写的URL
登录的html页面:
login.html
登录页面
登录的成功页面:
登录成功
登录的Servlet:
package com.qf.web.servlet;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
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;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//doGet(request, response);
request.setCharacterEncoding("utf-8");
String username=request.getParameter("email");
String pass=request.getParameter("psw");
System.out.println(request.getRemoteAddr()+":登录:账号:"+username+"----->密码:"+pass);
//将数据存储在Session中
HttpSession session=request.getSession();
System.out.println("Id:"+session.getId());//唯一标记,
System.out.println("getLastAccessedTime:"+session.getLastAccessedTime());//最后一次访问时间,毫秒
System.out.println("getMaxInactiveInterval:"+session.getMaxInactiveInterval());//获取最大的空闲时间,单位秒
System.out.println("getCreationTime:"+session.getCreationTime());//获取Session的创建,单位毫秒
//设置数据
session.setAttribute("username", username);
if(username.equals("admin")&&pass.equals("888")){
//重定向
response.sendRedirect("index.html");
}else{
request.getRequestDispatcher("login.html").forward(request, response);
}
}
}
注销登录的Servlet:
/**
* Servlet implementation class ExitLogin
*/
@WebServlet("/exit")
public class ExitLogin extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public ExitLogin() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//移除登录信息
request.getSession().removeAttribute("username");
request.getSession().invalidate();//将当前的Sessio失效
response.sendRedirect("login.html");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//doGet(request, response);
}
生成验证码:
/**
* Servlet implementation class CreateCode
* 验证码的生成
*/
@WebServlet("/createcode")
public class CreateCode extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public CreateCode() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
ValidateCode vc=new ValidateCode(200, 30, 4, 10);
String code=vc.getCode();
System.out.println(request.getRemoteAddr()+":生成:"+code);
//使用Session存储生成的验证码
HttpSession session=request.getSession();
session.setAttribute("code",code);
vc.write(response.getOutputStream());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
ServletContext:Servlet上下文,代表当前整个应用程序。(jsp中application)
ServletContext:Servlet上下文。
当WEB服务器启动时,会为每一个WEB应用程序(webapps下的每个目录就是一个应用程序)创建一块共享的存储区域
ServletContext也叫做“公共 区域”,也就是同一个WEB应用程序中,所有的Servlet和JSP都可以共享同一个区域。
ServletContext在WEB服务器启动时创建,服务器关闭时销毁。
方式一:GenericServlet提供了getServletContext()方法。(推荐)
方式二:ServletConfig提供了getServletContext()方法。
方式三:HttpSession提供了getServletContext()方法。
方式四:HttpServletRequest提供了getServletContext()方法。(推荐)
作用:
1、获取真实路径
获取当前项目的发布路径
request.getServletContext().getRealPath("/");
2、获取容器的附加信息
System.out.println(request.getServletContext().getServerInfo());
System.out.println(request.getServletContext().getContextPath());
System.out.println(request.getContextPath());
3、全局容器
//设置信息到全局容器中
request.getServletContext().setAttribute("msg", "共享信息");
//获取数据
System.out.println(request.getServletContext().getAttribute("msg"));
//移除数据
request.getServletContext().removeAttribute("msg");
特点:
唯一性: 一个应用对应一个servlet上下文。
一直存在: 只要容器不关闭或者应用不卸载,servlet上下文就一直存在。
web.xml文件配置servletContext参数
appname
xxx管理系统
appversion
2.0
//获取servlet上下文参数
String appname=application.getInitParameter("appname");
String appversion=application.getInitParameter("appversion");
System.out.println(appname+"..."+appversion);
package com.qf.servlet;
import java.io.IOException;
import java.io.PrintWriter;
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;
/**
* Servlet implementation class Servlet3
*/
@WebServlet("/servlet3")
public class Servlet3 extends HttpServlet {
private static final long serialVersionUID = 1L;
public Servlet3() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
ServletContext application = request.getServletContext();
Integer count=(Integer) application.getAttribute("count");
if(count==null) {
count=1;
application.setAttribute("count", count);
}else {
count++;
application.setAttribute("count", count);
}
PrintWriter out=response.getWriter();
out.write("servlet共访问次数:"+count);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
1 Cookie:客户端状态管理,保存一小段信息,第一次访问时服务器,服务器cookie响应给浏览器,下次再访问服务器时,把cookie回传给服务器,服务器收到cookie。
Cookie cookie=new Cookie("xxx","141241232");
cookie.setMaxAge(); 默认-1 保存在内存中, 0 表示失效 正数 过期时间 单位是秒
cookie.setPath();设置有效路径 cookie.setPath("/“)
response.addCookie(cookie);
2 session:服务器端状态管理,是一个map集合,session使用sessionid区别,服务器会把SessionId作为cookie的值发送给浏览器,浏览器再次访问服务器时,服务器读取到cookie(sessionid)。
保存登录信息,购物车
HttpSession session=request.getSession();
3 ServletContext:servlet上下文:是所有用户共享的区域,是一个map集合。
3.1获取ServletContext:
this.getServletContext();
request.getServletContext();
3.2 作用
request.getServletContext.getRealPath();//获取项目的发布路径
request.getServletContext.getServerInfo();//服务器信息
request.getServletContext.getContextPath();//上下文路径 项目路径
request.getContextPath();//同上
3.3 容器使用
servletContext.getInitparamter("name");
3.4配置ServletContext启动参数 web.xml
request.getServletContext.getInitparamenter("appname");