动态网站开发讲课笔记09:会话及会话技术——Session对象

一、什么是Session

  • 目标:了解什么是Session

1、Session对象

  • 当人们去医院就诊时,就诊病人需要办理医院的就诊卡,就诊卡上只有卡号,没有其他信息。但病人每次去该医院就诊时,只要出示就诊卡,医务人员便可根据卡号查询到病人的就诊信息。Session技术类似医院办理就诊卡和医院为每个病人保留病历档案的过程。当浏览器访问Web服务器时,Servlet容器就会创建一个Session对象和ID属性, Session对象就相当于病历档案,ID就相当于就诊卡号。当客户端后续访问服务器时,只要将ID传递给服务器,服务器就能判断出该请求是哪个客户端发送的,从而选择与之对应的Session对象为其服务。

2、Session的优点

  • Session还具有更高的安全性,它将关键数据保存在服务器。cookie则是将数据存在客户端的浏览器中。因此cookie是较为危险的,若客户端遭遇黑客攻击,cookie信息容易被窃取,数据也可能被篡改,而运用Session可以有效避免这种情况的发生。

二、了解HTTPSession API

  • 目标:掌握HttpSession接口中的常用方法

1、Session对象的getSession()方法

  • Session是与每个请求消息紧密相关的,为此,HttpServletRequest定义了用于获取Session对象的getSession()方法,该方法有两种重载形式。
方法 说明
public HttpSession getSession(boolean create) 根据传递的参数判断是否创建新的HttpSession对象,如果参数为true,则在相关的HttpSession对象不存在时创建并返回新的HttpSession对象,否则不创建新的HttpSession对象,而是返回null。
public HttpSession getSession() 相当于第一个方法参数为true时的情况,在相关的HttpSession对象不存在时总是创建新的HttpSession对象。需要注意的是,由于getSession()方法可能会产生发送会话标识号的Cookie头字段,所以必须在发送任何响应内容之前调用getSession()方法。

2、HttpSession接口中的常用方法

方法声明 功能描述
String getId() 用于返回与当前HttpSession对象关联的会话标识号
long getCreationTime() 用于返回Session创建的时间,这个时间是创建Session的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式
long getLastAccessedTime() 用于返回客户端最后一次发送与Session相关请求的时间,这个时间是发送请求的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式
void setMaxInactiveInterval(int interval) 用于设置当前HttpSession对象可空闲的以秒为单位的最长时间,也就是修改当前会话的默认超时间隔
boolean isNew() 判断当前HttpSession对象是否是新创建的
void invalidate() 用于强制使Session对象无效
ServletContext getServletContext() 用于返回当前HttpSession对象所属于的Web应用程序对象,即代表当前Web应用程序的ServletContext对象
void setAttribite(String name, Object value) 用于将一个对象与一个名称关联后存储到当前的HttpSession对象中
String getAttribute() 用于从当前HttpSession对象中返回指定名称的属性对象
void removeAttribute(String name) 用于从当前HttpSession对象中删除指定名称的属性

三、 Session的生命周期

  • 目标:掌握Session对象的生效与失效

1、Session生效

  • Sessinon在用户第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session。此外,还可调用request.getSession(true)强制生成Session。只访问HTML、IMAGE等静态资源并不会创建Session。

2、Session失效

(1)“超时限制”判断Session是否生效

  • Web服务器采用“超时限制”判断客户端是否还在继续访问。在一定时间内,如果某个客户端一直没有请求访问,那么,Web服务器就会认为该客户端已经结束请求,并且将与该客户端会话所对应的HttpSession对象变成垃圾对象,等待垃圾收集器将其从内存中彻底清除。反之,如果浏览器超时后,再次向服务器发出请求访问,那么,Web服务器会创建一个新的HttpSession对象,并为其分配一个新的ID属性。

(2)强制Session失效

  • 使用invalidate()方法可以强制使Session对象失效
HttpSession session = request.getSession();
session.invalidate(); // 注销该request的所有session

四、Session案例演示 - 实现购物车

1、创建Web项目

  • 创建Java Enterprise项目,添加Web Application
    在这里插入图片描述

  • 设置项目名称与保存位置
    在这里插入图片描述

2、修改Artifact名称,重新部署项目

  • 在项目结构窗口里修改Artifact名称
    在这里插入图片描述

  • 修改首页,启动服务器,查看结果
    在这里插入图片描述

3、创建蛋糕实体类

  • 创建net.hdl.session.bean包,然后在包里创建Cake类
    在这里插入图片描述
package net.hdl.session.bean;

/**
 * 功能:蛋糕实体类
 * 作者:胡德兰
 * 日期:2023年04月14日
 */
public class Cake {
    private static final long serialVersionUID = 1L;
    private String id;
    private String name;

    public Cake() {
    }

    public Cake(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Cake{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

  • 创建net.hdl.session.test包,然后在包里创建TestCake类
    在这里插入图片描述
package net.hdl.session.test;

import net.hdl.session.bean.Cake;

/**
 * 功能:测试蛋糕实体类
 * 作者:胡德兰
 * 日期:2023年05月23日
 */
public class TestCake {
    public static void main(String[] args) {
        // 1. 基于无参构造方法创建蛋糕对象
        Cake cake1 = new Cake();
        // 设置蛋糕对象属性
        cake1.setId("001");
        cake1.setName("金玫瑰蛋糕");
        // 打印蛋糕对象
        System.out.println(cake1);
        // 2. 基于有参构造方法创建蛋糕对象
        Cake cake2 = new Cake("002", "黄苹果蛋糕");
        // 打印蛋糕对象
        System.out.println(cake2);
    }
}

  • 运行程序,查看结果
    在这里插入图片描述

4、创建蛋糕数据库模拟类

  • 在net.hdl.session根包里创建dao包,然后在子包里创建CakeDao类,用于模拟访问蛋糕数据库
    在这里插入图片描述
package net.hdl.session.dao;

import net.hdl.session.bean.Cake;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 功能:蛋糕数据访问对象
 * 作者:胡德兰
 * 日期:2023年04月14日
 */
public class CakeDao {

    private static Map<String, Cake> cakes = new LinkedHashMap<String, Cake>();

    static {
        cakes.put("1", new Cake("1", "A类蛋糕"));
        cakes.put("2", new Cake("2", "B类蛋糕"));
        cakes.put("3", new Cake("3", "C类蛋糕"));
        cakes.put("4", new Cake("4", "D类蛋糕"));
        cakes.put("5", new Cake("5", "E类蛋糕"));
    }

    // 获得所有蛋糕
    public static Collection<Cake> findAllCakes() {
        return cakes.values();
    }

    // 按编号获取蛋糕
    public static Cake findCakeById(String id) {
        return cakes.get(id);
    }
}

  • 在net.hdl.session.test包里创建TestCakeDao类
    在这里插入图片描述
package net.hdl.session.test;

import net.hdl.session.bean.Cake;
import net.hdl.session.dao.CakeDao;

import java.util.Collection;

/**
 * 功能:测试蛋糕用户访问对象
 * 作者:胡德兰
 * 日期:2023年05月23日
 */
public class TestCakeDao {
    public static void main(String[] args) {
        // 查询全部蛋糕
        Collection<Cake> cakes = CakeDao.findAllCakes();
        // 通过增强for循环遍历蛋糕集合
        for (Cake cake : cakes) {
            System.out.println(cake.getId() + "\t" + cake.getName());
        }
        System.out.println();
        // 按编号查询蛋糕
        String id = "4";
        Cake cake = CakeDao.findCakeById(id);
        // 输出蛋糕对象属性
        System.out.println(cake.getId() + "\t" + cake.getName());
    }
}

  • 运行程序,查看结果
    在这里插入图片描述

5、创建蛋糕列表处理程序

  • 创建net.hdl.session.servlet包,然后在包里创建CakeListServlet类,用于显示所有可购买蛋糕的列表,通过单击“购买”链接,便可将指定的蛋糕添加到购物车中
    在这里插入图片描述
package net.hdl.session.servlet;

import net.hdl.session.bean.Cake;
import net.hdl.session.dao.CakeDao;

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 java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * 功能:显示蛋糕列表
 * 作者:胡德兰
 * 日期:2023年05月23日
 */
@WebServlet(name = "CakeListServlet", urlPatterns = "/cake_list")
public class CakeListServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置响应体内容类型
        response.setContentType("text/html; charset=utf-8");
        // 获取字符输出流
        PrintWriter out = response.getWriter();
        // 获取蛋糕集合
        Collection<Cake> cakes = CakeDao.findAllCakes();
        out.print("<body style='text-align: center'>");
        out.print("<h3>本站提供的蛋糕</h3>");
        // 显示全部蛋糕
        for (Cake cake : cakes) {
            String url = "purchase?id=" + cake.getId();
            out.print(cake.getId() + " " + cake.getName()
                    + " <a href='" + url + "'>点击购买</a><br>");
        }
        out.print("</body>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
}

  • 运行程序,查看结果
    在这里插入图片描述

6、创建购物处理程序

  • 在net.hdl.session.servlet包里创建PurchaseServlet类
    在这里插入图片描述
package net.hdl.session.servlet;

import net.hdl.session.bean.Cake;
import net.hdl.session.dao.CakeDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 功能:购物处理程序
 * 作者: 胡德兰
 * 日期: 2023年04月21 日
 */
@WebServlet(name = "PurchaseServlet",urlPatterns = "/purchase")
public class PurchaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取用户是否购买蛋糕的id
        String id=request.getParameter("id");
        //判断用户是否选购
        if(id!=null){
            //按照id获取蛋糕对象
            Cake cake= CakeDao.findCakeById(id);
            //创建或获取用户的Session对象
            HttpSession session=request.getSession();
            //从Session对象中获取用户的购物车
            List<Cake> cart=(List<Cake>) session.getAttribute("cart");
            //判断购物车是否为空
            if(cart==null){
                //为用户创建一个购物车(LIST集合模拟购物车)
                cart=new ArrayList<>();
                //将购物车存入Session对象
                session.setAttribute("cart",cart);
            }
            //将选购的蛋糕放入购物车
            cart.add(cake);
            //创建Cookie用来保存Session的标识号
            Cookie cookie=new Cookie("JSESSIONID",session.getId());
            //设置cookie属性
            cookie.setMaxAge(30*60);//30分钟
            cookie.setPath("/Servlet");
            //加入cookie
            response.addCookie(cookie);
            //从定向到购物车页面(CartSerclet,其中uel为'cart')
            response.sendRedirect("cart");
        }else {
            //从定向到蛋糕列表显示页面(CakeListServlet,其url为'cakr_list')
            response.sendRedirect("cake_list");
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request,response);

    }
}

7、创建购物车处理程序

  • 在net.hdl.session.servlet包里创建CartServlet类
    在这里插入图片描述
package net.hdl.session.servlet;

import net.hdl.session.bean.Cake;

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;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * 功能:
 * 作者:胡德兰
 * 日期:2023年04月14日
 */
@WebServlet(name = "CartServlet", urlPatterns = "/cart")
public class CartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置响应体内容类型
        response.setContentType("text/html; charset=utf-8");
        // 获取字符输出流
        PrintWriter out = response.getWriter();
        // 变量cart引用用户的购物车
        List<Cake> cart = null;
        // 变量hasPurchased标记用户是否买过商品
        boolean hasPurchased = true;
        // 获得用户的session
        HttpSession session = request.getSession(false);
        // 判断会话是否为空
        if (session == null) { // 尚未购买过商品
            hasPurchased = false;
        } else {
            // 获得用户购物车
            cart = (List) session.getAttribute("cart");
            // 判断购物车是否为空
            if (cart == null) { // 尚未购买过商品
                hasPurchased = false;
            }
        }

        out.print("<body style='text-align: center'>");
        // 判断用户是否购买过商品
        if (!hasPurchased) {
            out.print("遗憾,您尚未购买任何商品~<br>");
        } else {
            // 显示用户购买蛋糕的信息
            out.print("<h3>您购买的蛋糕</h3>");
            for (Cake cake : cart) {
                out.print(cake.getId() + " " + cake.getName() + "<br>");
            }
        }
        out.print("</body>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
}

8、启动服务器,查看结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述