typora-copy-images-to: img
typora-root-url: img
知识回顾
request&http&response&ServletContext
1.request域对象:一次请求和响应使用。
用来存储数据setAttribute(name,value) 获取数据getAttribute(name) 删除数据
2.http响应报文协议:
响应行:状态码 200 OK 302 重定向 404找不到资源 500 服务器异常 304 浏览器缓存
响应头:key:value
refresh ---- 秒数;url=服务器地址
content-type---告知浏览器文件的MIME类型(文件在服务器中的文件类型 text/plain text/html)
location:结合302重定向
content-disposition:告知浏览器以附件下载:“attachment;filename=文件名”
响应体:
getWriter()响应字符数据
getOutputStream()响应字节数据
3.response对象:服务器响应给浏览器数据的对象
1)setStatus(状态码)
2)setHeader(key,value); setContentType("text/html;charset=utf-8");
sendRedirect()重定向
4.ServletContext:域对象,表示整个web项目,并且每个web项目只有一个ServletContext对象,
tomcat启动创建对象,关闭tomcat对象消失。多次请求和响应
Arrays的asList方法【掌握】
注意一:
1.使用数组工具类Arrays中的asList方法将数组转换为集合,因为集合的数据是来自于数组,数组长度固定不变,所以这里要求集合的长度不能改变
2.如果将基本数据类型的数组转换为集合,那么数组类型不能是基本数据类型,必须是包装类类型注意二:
包装类缓冲池 byte 常量池 :
针对byte范围的数据,jvm专门单独开辟一个byte常量池。其实就是一个数组。
查阅源码,针对-128到127之间的数据做了一个数据缓冲池,
如果数据是该范围内的,每次并不创建新的空间。直接从数组中拿数据。反之,需要创建新的空间。例如:Integer的数据直接赋值,如果在-128到127之间,会直接从缓冲池里获取数据
package com.itheima.sh.demo_02;
public class Demo01 {
public static void main(String[] args) {
//包装类面试题
Integer i1 = new Integer(50);
Integer i2 = new Integer(50);
System.out.println(i1 == i2); //false 凡是new的,都走堆内存中创建一块新的空间
Integer i3 = new Integer(500);
Integer i4 = new Integer(500);
System.out.println(i3 == i4); //false
--------------------------------------------------------------------------------
// 发生了自动装箱 ,实现原理:Integer static Integer valueOf(int i)
// 返回一个表示指定的 int 值的 Integer 实例
/*
源码:
class Integer{
//Integer类中的成员内部类
private static class IntegerCache{
static final int low = -128;
static final int high;
static final Integer cache[];
//静态代码块
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
//上述代码调用这个函数创建
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
//i的值在-128和127之间
if (i >= IntegerCache.low && i <= IntegerCache.high){
/*如果i在-128和127之间,则执行这个return语句,将i的值存在
某个数组空间中,下次i的值相同情况下,继续返回相同空间的值*/
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
}
}
*/
解析:
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127)
//传递的值在-128到127直接直接到数组cache中获取内容
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
--------------------------------------------------------------------------------
Integer i5 = 50; //Integer.valueOf(50); -128-127自动装箱
Integer i6 = 50;
System.out.println(i5 == i6); //true 因为i5和i6的值都是50,到静态数组cache获取同一个空间数据
--------------------------------------------------------------------------------
// 针对-128到127之间的数据做了一个数据缓冲池,
// 如果数据是该范围内的,每次并不创建新的空间。直接从数组中拿数据。反之,需要创建新的空间。
//new Integer(i) ---> new Integer(500)
Integer i7 = 500;
//new Integer(i) ---> new Integer(500)
Integer i8 = 500;
System.out.println(i7 == i8);//false
}
}
package com.itheima.sh.c_cookie_test_03;
import java.util.Arrays;
import java.util.List;
/*
注意:
1.使用数组工具类Arrays中的asList方法将数组转换为集合,因为集合的数据是来自于数组,数组长度固定不变,
所以这里要求集合的长度不能改变
2.如果将基本数据类型的数组转换为集合,那么数组类型不能是基本数据类型,必须是包装类类型
*/
public class Demo02 {
public static void main(String[] args) {
method_3();
method_2();
method_1();
}
private static void method_3() {
//1.定义整数数组
// Integer[] arr = {10,20,30}; //自动装箱Integer.valueOf(10) -128-127byte常量池 new Integer();
Integer[] arr = {Integer.valueOf(10),Integer.valueOf(20),Integer.valueOf(30)};
//2.转化为集合
// public static <T> List<T> asList(T... a) {}
List<Integer> list = Arrays.asList(arr);
System.out.println(list.size()); //3
System.out.println(list); //{10,20,30}
}
private static void method_2() {
//1.定义整数数组
int[] arr = {10,20,30};
//2.转化为集合
// public static <T> List<T> asList(T... a) {}
List<int[]> list = Arrays.asList(arr);
System.out.println(list.size()); //1
System.out.println(list); //[[I@1540e19d]
}
private static void method_1() {
//1.创建数组
String[] arr = {"abc","def","abc"};
//2.将上述数组转换为List集合
List<String> list = Arrays.asList(arr);
//给list集合添加数据
// list.add("哈哈"); //Exception in thread "main" java.lang.UnsupportedOperationException
// list.remove("def"); //Exception in thread "main" java.lang.UnsupportedOperationException
//获取
// System.out.println(list.get(0));
//修改元素内容不是集合长度
list.set(0, "呵呵");
//3.输出集合数据
System.out.println("list = " + list);
}
}
一、Cookie技术
1、什么是会话【掌握】
用户打开浏览器访问网站开始直到关闭浏览器的过程就是一次会话。
2、Cookie介绍
Cookie属于一个类,可以直接创建对象
Cookie是在服务器端创建,保存在浏览器端
tomcat服务器将创建的cookie以及cookie中的数据响应给浏览器,
当使用HttpServletResponse中的对象调用方法addCookie将服务器创建的cookie直接保存到浏览器端
当我们下次访问同一个服务器会携带相同的cookie信息,在服务器中我们可以使用HttpServletRequest对象调用方法getCookies获取随着请求携带过来的所有的cookie。
好处:减轻服务器压力
弊端:不安全
3、Cookie的应用场景
【1】购物车 : 例如现在的苏宁易购,不登录账户也可浏览商品,并添加购物车。
【2】记住用户名和密码:例如 b站 用户登陆页面,勾选框,浏览器可记住用户名和密码。
说明:目前还不能完成第二次访问登录页面,在页面中取出cookie中的用户名和密码。
4、Cookie的常用API(重要)
【1】需求:上述应用场景中我们目前还不能完成第二次访问登录页面,在页面中取出cookie中的用户名和密码。我们可以实现第二次访问服务器取出cookie中的数据。
【2】图解
方法 | 使用示例 | 说明 |
---|---|---|
创建Cookie对象 | Cookie(String name,String value)name表示指定 cookie 名称 ,value 表示指定 cookie 值 | Cookie c1 = new Cookie(“username”,“suoge”) |
获取cookie的name值 | String getName() | c1.getName() |
获取cookie的value值 | String getValue() | c1.getValue() |
设置cookie的值 | void setValue(String value) | c1.setValue(“李四”) |
【3】构造方法
Cookie(String name, String value)
【4】操作Cookie类中的name和value方法
1.String getName() // 获取cookie类中的name值
2.String getValue() // 获取cookie类中的value值
3.void setValue(String newValue) // 修改value值
【5】使用HttpServletResponse中的对象调用方法addCookie将服务器创建的cookie直接保存到浏览器端
void addCookie(Cookie cookie) 参数需要一个cookie类的对象
【6】使用HttpServletRequest对象调用方法getCookies获取随着请求携带过来的所有的cookie。
Cookie[] getCookies() 获取随着请求的所有cookie放到数组中
【7】案例实现
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/loginServlet" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
记住用户名和密码:<input type="checkbox" name="check"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
loginServlet:
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.处理post请求乱码
request.setCharacterEncoding("utf-8");
//2.获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//3.获取复选框的value属性值
String check = request.getParameter("check");
//4.判断用户是否希望记住用户名和密码
if("on".equals(check)){
//5.假设用户希望记住,我们创建cookie对象将用户名和密码存储到cookie中
//Cookie(String name, String value)
Cookie cookie1 = new Cookie("username", username);
Cookie cookie2 = new Cookie("password", password);
//6.响应给浏览器 void addCookie(Cookie cookie) 参数需要一个cookie类的对象
response.addCookie(cookie1);
response.addCookie(cookie2);
}
}
}
login02Servlet:
@WebServlet("/login02Servlet")
public class Login2Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取页面中所有的cookie 使用HttpServletRequest对象调用方法getCookies获取随着请求携带过来的所有的cookie。
//Cookie[] getCookies() 获取随着请求的所有cookie放到数组中
Cookie[] cookies = request.getCookies();
//2.遍历数组取出用户名和密码的cookie
for (Cookie cookie : cookies) {
//3.输出用户名和密码的cookie中的value值和name值
String cookieName = cookie.getName();
//判断是否是用户名
if("username".equals(cookieName)){
System.out.println(cookieName+"----"+cookie.getValue());
}else if("password".equals(cookieName)){
//判断是否是密码
System.out.println(cookieName+"----"+cookie.getValue());
}
}
}
}
5、关于cookie中存储特殊字符问题 (理解原理)
如果直接向cookie中存储特殊字符,例如空格,分号(😉,逗号(,),等号(=)等特殊字符。那么就会出现问题。
在向cookie中存储特殊字符之前必须要先进行编码处理,然后从cookie中取出之后在进行解码处理。
1、cookie中不能直接存储特殊字符:空格 分号等,如果存储必须先编码在存储:
String encode = URLEncoder.encode(str, "utf-8");
2、在获取的时候需要解码:
String decode = URLDecoder.decode(value, "utf-8");
【1】向cookie中存储特殊字符问题演示
@WebServlet("/specialCookie01Servlet")
public class SpecialCookie01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
向cookie中存储特殊字符问题演示
*/
//1.创建Cookie类的对象
Cookie cookie = new Cookie("msg", "12 34");
//2.将cookie存储到浏览器端
response.addCookie(cookie);
}
}
【2】解决向cookie中存储特殊字符的问题
方案:在向cookie中存储特殊字符前进行编码,然后取出之后需要解码。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
向cookie中存储特殊字符问题演示
*/
//1.创建Cookie类的对象
// Cookie cookie = new Cookie("msg", "12 34");报错
String str = "12 34";
//编码
String encode = URLEncoder.encode(str, "utf-8");
Cookie cookie = new Cookie("msg", encode);
//2.将cookie存储到浏览器端
response.addCookie(cookie);
}
}
@WebServlet("/specialCookie02Servlet")
public class SpecialCookie02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取浏览器的cookie
Cookie[] cookies = request.getCookies();
//2.遍历cookie数组
for (Cookie cookie : cookies) {
//3.取出cookie的name
String cookieName = cookie.getName();
//4.判断cookieName的值是否是msg
if("msg".equals(cookieName)){
//5.取出value
String value = cookie.getValue();
//6.解码并输出
String decode = URLDecoder.decode(value, "utf-8");
System.out.println(decode);
}
}
}
}
6、Cookie的存活时间setMaxAge() 【重要】
【1】需求:向cookie中存储数据,并设置cookie的存活时间为1周;
cookie默认的存活时间是会话结束即关闭浏览器。
我们平常开发中对于某些cookie中存储的数据希望保存的时间更长一些,而不是浏览器关闭就不存在。
如果想让cookie长久保存我们可以使用Cookie类的方法即可
cookie分为两种
>1)会话级别的cookie:关闭浏览器cookie自动销毁
>
>2)**持久化级别的cookie**:**通过Cookie类的方法setMaxAge设置cookie的存活时间**
void setMaxAge(int expiry) 参数是秒
@WebServlet("/cookiePersis01Servlet")
public class CookiePersis01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//【1】需求:向cookie中存储数据,并设置cookie的存活时间为1周;持久化cookie
//1.创建cookie对象
Cookie cookie = new Cookie("age", "18");
//2.设置cookie的最大存活时间 void setMaxAge(int expiry) 参数是秒
cookie.setMaxAge(60*60*24*7);
//3.响应cookie给浏览器
response.addCookie(cookie);
}
}
7、设置cookie的有效路径【理解】
如果访问的servlet路径是要携带的cookie的路径本身或者子路径,此时会携带该cookie,
否则不会携带cookie
setPath(“有效路径”)
【1】如果系统特别复杂的情况下,我只希望访问系统的用户模块时携带用户的cookie,不需要携带其他模块的cookie。
设置cookie有效路径:/user 即可。
设置cookie的有效路径,使用Cookie类中的方法:
void setPath(String uri) 参数中书写有效路径
【2】需求:
- 在PathServlet中创建一个Cookie,设置路径为"/suoge/a/b";
- 新建一个PathTwoServlet,设置该servlet的访问路径:"/suoge/a/b/d";
- 新建一个PathThrServlet,设置该servlet的访问路径:/suoge/a";
- 分别在PathTwoServlet,PathThrServlet中获取cookie;
@WebServlet("/pathServlet")
public class PathServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//在PathServlet中创建一个Cookie,设置路径为"/suoge/a/b";
//1.创建cookie对象
Cookie cookie = new Cookie("name", "柳岩");
//2.设置有效路径 void setPath(String uri) 参数中书写有效路径
cookie.setPath("/suoge/a/b");
//3.响应给浏览器
response.addCookie(cookie);
}
}
@WebServlet("/suoge/a/b/d")
public class PathTwoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//新建一个PathTwoServlet,设置该servlet的访问路径:"/suoge/a/b/d";
//1.获取浏览器所有的cookie
Cookie[] cookies = request.getCookies();
/*
通过演示我们发现这里获取的cookie:
age 路径是 / 只要访问同一个ip地址或者域名都会携带cookie信息
name 路径是 /suoge/a/b 我们这里访问的servlet路径是:/suoge/a/b/d 因为
cookie的name有效路径是/suoge/a/b,而访问的servlet路径属于其子路径。只要访问cookie
有效路径的本身以及子路径都会携带cookie
*/
System.out.println(Arrays.toString(cookies));
}
}
@WebServlet("/suoge/a")
public class PathThrServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//新建一个PathTwoServlet,设置该servlet的访问路径:"/suoge/a/b/d";
//1.获取浏览器所有的cookie
Cookie[] cookies = request.getCookies();
/*
通过演示我们发现这里获取的cookie:
age 路径是 / 只要访问同一个ip地址或者域名都会携带cookie信息
name 路径是 /suoge/a/b 我们这里访问的servlet路径是:/suoge/a 因为
cookie的name有效路径是/suoge/a/b,而访问的servlet路径不属于其子路径以及本身路径。所以不会携带cookie
*/
// System.out.println(Arrays.toString(cookies));
}
}
面试题:
当给cookie设置有效路径"/user/role"后:
访问的servlet路径是:
a:/user 不会携带
b:/user/aaa 不会携带
c:/user/role 会携带
d: /role 不会携带
e:/user/role/aaa 会携带
8、Cookie删除(重要)
1.如果cookie是会话级别的cookie,关闭浏览器会话结束,cookie消失
2.在浏览器手动清除
**3.使用代码进行删除 **【思想是替换。把持久化的cookie变为会话级别的cookie】。
@WebServlet("/deleteCookie01Servlet")
public class DeleteCookie01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//3.使用代码进行删除
//思想是替换。把持久化的cookie变为会话级别的cookie。
//步骤:
//1.创建cookie new Cookie(name,value), name值就是要删除的cookie的name,value的值是"" 空字符串
Cookie cookie = new Cookie("age", "");
//2.设置cookie的存活时间是0
cookie.setMaxAge(0);
//3.设置cookie的有效路径和原来的有效路径一致
cookie.setPath("/");
//4.响应给浏览器
response.addCookie(cookie);
}
}
9、案例【cooki记录浏览商品的历史记录】
需求:做一个商品页面,当我们访问后,在页面上点击查看商品浏览记录后,可以查看到以前浏览过的商品信息
【1】分析
1.创建两个页面:
goods.html 展示商品信息
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/goodsServlet?good=galaxy">三星手机</a> <br>
<a href="/goodsServlet?good=chuizi">锤子手机</a> <br>
<a href="/goodsServlet?good=xiaomi">小米手机</a> <br>
</body>
</html>
goods2.html 点击该页面的按钮就可以查看浏览商品的历史记录
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="goods.html">继续浏览</a> <br>
<a href="/historyServlet">查看浏览记录</a> <br>
</body>
</html>
2.创建2个servlet:
goodsServlet:当用户在goods.html页面中点击浏览商品,执行该servlet,将浏览的商品存储到cookie中,然后响应给浏览器。
import java.util.Arrays;
import java.util.List;
/*
goodsServlet:当用户在goods.html页面中点击浏览商品,执行该servlet,将浏览的商品存储到cookie中,然后响应给浏览器。
*/
@WebServlet("/goodsServlet")
public class GoodsServle extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取浏览的商品名
String good = request.getParameter("good");
//2.获取页面中的所有cookie
Cookie cookieHistory = CookieUtils.getCookies(request,"history");
/*
3.判断获取的cookie是否是null
如果是null 说明之前没有浏览过商品,第一次浏览
如果不是null 说明之前浏览过商品
*/
if(cookieHistory == null){
//4.如果是null 说明之前没有浏览过商品,第一次浏览,创建Cookie将商品存储到cookie中
Cookie cookie = new Cookie("history", good);
//5.设置cookie最大存活时间
cookie.setMaxAge(60*60);
//6.响应给浏览器
response.addCookie(cookie);
}else{
/*
7.如果不是null 说明之前浏览过商品
name value
cookie 中的数据: history galaxy_chuizi
*/
//获取cookie中的value ===》galaxy_chuizi
String value = cookieHistory.getValue();
//8.按照_下划线字符进行切割变为数组 {galaxy,chuizi,...}
String[] arr = value.split("_");
//9.将数组转换为集合List static <T> List<T>asList(T... a)
List<String> list = Arrays.asList(arr);
//10.使用List集合中的方法 boolean contains(Object o)
// 判断集合list中是否含有当前浏览的商品
if(!list.contains(good)){
//11.不包含 {galaxy,chuizi} 不包含xiaomi 我们要将xiaomi拼接到value值
//value = galaxy_chuizi_xiaomi===>history====>galaxy_chuizi_xiaomi
value=value+"_"+good;
//将新的value覆盖之前的value
cookieHistory.setValue(value);
//12.将新的cookie响应给浏览器
response.addCookie(cookieHistory);
}
}
}
}
工具类:
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/*
工具类用来判断浏览器中是否含有name是history的cookie,如果有则返回,没有返回null
*/
public class CookieUtils {
String cookieNameHistory="history"
public static Cookie getCookies(HttpServletRequest request, String cookieNameHistory) {
//1.获取浏览器所有的cookie
Cookie[] cookies = request.getCookies();
//防止空指针异常我们这里需要对数组cookies判断
if(cookies!=null && cookies.length>0){
//2.遍历数组取出每个cookie
for (Cookie cookie : cookies) {
//3.取出cookie的name值
String name = cookie.getName();
//4.判断name是否等于cookieNameHistory即history
if(cookieNameHistory.equals(name)){
//5.说明含有name是history的cookie,返回cookie
return cookie;
}
}
}
//能够执行到这里说明浏览器没有name是history的cookie
return null;
}
}
historyServlet:在 goods2.html页面中点击历史记录按钮,执行该servlet,在该servlet中取出浏览器客户端的历史记录的cookie并将商品历史记录响应给浏览器。
/*
historyServlet:在 goods2.html页面中点击历史记录按钮,执行该servlet,在该servlet中取出浏览器客户端的历史记录的cookie并将商品历史记录响应给浏览器。
*/
@WebServlet("/historyServlet")
public class HistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取cookie
Cookie cookieHistory = CookieUtils.getCookies(request, "history");
//2.判断cookie是否等于null
if(cookieHistory == null){
//3.等于null,说明没有历史记录,响应给浏览器一句话
response.getWriter().print("<a href=\"/goods.html\">您还没有浏览记录,去浏览吧</a>");
}else{
//4.不等于null,说明有历史记录,取出cookie的value galaxy_chuizi_xiaomi
String value = cookieHistory.getValue();
//5.按照_下划线切割为数组
String[] arr = value.split("_");
//6.响应给浏览器
response.getWriter().print("您浏览的商品记录是:"+Arrays.toString(arr));
response.getWriter().print("<br><a href=\"/goods.html\">继续浏览</a>");
}
}
}
二、Session技术
1、session介绍
session属于在服务器中创建的,存在服务器中。属于会话过程中的技术,用来存储数据。
1.Session会话技术属于HttpSession接口类型
2.属于在服务器中创建的,存在服务器中,并且可以保留一段时间(默认30min)
2、Session原理【掌握】
1.用户第一次访问的时候,tomcat会创建对应的session容器,每个容器具有唯一的标识JSESSIONID,然后tomcat底层创建会话级别的cookie存储唯一标识JSESSIONID存储到浏览器端。
2.用户再次访问,tomcat中取出session并从cookie中取出之前保存的唯一标识JSESSIONID进行比较查找自己的session容器
3、Session常用API【掌握】
创建和销毁的API
【1】request.getSession() 【记住】
// 创建session 如果session不存在,创建session,存在,获取当前session
request.getSession(); // 第一次创建 ,后续获取
【2】HttpSession session1 = request.getSession(true);
//两个API效果相同 request.getSession(true); 等于 request.getSession();
// 创建session 如果session不存在,创建session,存在,获取当前session
HttpSession session1 = request.getSession(true);
【3】request.getSession(false); 了解
// 如果当前存在session 获取当前session, 不存在,不获取session,返回null
request.getSession(false);
【4】session.invalidate() ;
//使当前session失效,即销毁当前session
session.invalidate()
【5】补充:String sessionId = session.getId();
// 获取session的唯一标识JSESSIONID
String sessionId = session.getId();
代码演示:
@WebServlet("/sessionDemo01Servlet")
public class SessionDemo01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
session创建和销毁的api演示:
*/
//1.创建session
HttpSession session = request.getSession();
//2.获取唯一标识并输出
System.out.println(session.getId());
//3.创建session 有则获取,没有则创建
HttpSession session2 = request.getSession(true);
//4.获取唯一标识并输出
System.out.println(session2.getId());
//销毁session session.invalidate()使当前session失效,即销毁当前session
session.invalidate();
//5.创建session 有则获取,没有则不获取返回null
HttpSession session3 = request.getSession(false);
System.out.println("session3 = " + session3);
if(session3!=null){
//6.获取唯一标识并输出
System.out.println(session3.getId());
}
}
}
HttpSession接口方法 | 作用 |
---|---|
long getCreationTime() | 表示会话创建的时间,返回long类型。 表示1970-1-1到这个时间之间相差的毫秒数 |
boolean isNew() | 判断当前是否是一个新的会话,是的返回true,否则就是false |
@WebServlet("/sessionDemo02Servlet")
public class SessionDemo02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
1.long getCreationTime()**表示会话创建的时间**,返回long类型。 表示1970-1-1到这个时间之间相差的毫秒数
2.boolean isNew()判断当前是否是一个新的会话,是的返回true
*/
//创建session
HttpSession session = request.getSession();
//long getCreationTime()**表示会话创建的时间**,返回long类型。 表示1970-1-1到这个时间之间相差的毫秒数
/*long creationTime = session.getCreationTime();
System.out.println("creationTime = " + creationTime);
//将creationTime毫秒转换为日期时间
Date date = new Date(creationTime);
System.out.println(date.toString());
//2020-11-20 14:52:46
System.out.println(date.toLocaleString());
//Timestamp(long time) 2020-11-20 14:54:30
System.out.println(new Timestamp(creationTime).toLocaleString());*/
//2.boolean isNew()判断当前是否是一个新的会话,是的返回true
System.out.println(session.isNew());
}
}
5、Session的域对象操作【掌握】
1.session作为域对象可以在多次请求中共享session容器中的数据。
2.三个域对象的范围:request < Session < ServletContext
【1】session属于域对象,具有三个方法:
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
【2】在一次会话过程中都可以使用,代码实现:
@WebServlet("/sessionScope01Servlet")
public class SessionScope01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建session
HttpSession session = request.getSession();
//2.向session中存储数据
session.setAttribute("username","小明");
System.out.println(session.getId());
//重定向到sessionScope02Servlet
response.sendRedirect("/sessionScope02Servlet");
}
}
@WebServlet("/sessionScope02Servlet")
public class SessionScope02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取session
HttpSession session = request.getSession();
//2.从session中获取数据
String username = (String) session.getAttribute("username");
System.out.println(session.getId());
}
}
6.Session的生命周期【了解】
1.session的创建时间:第一次访问执行代码request.getSession();
2.销毁session:
1)默认是30min,时间到自动销毁
2)执行方法:invalidate()
3) 服务器非正常关闭
4)手动设置过期时间
手动设置过期时间
HttpSession的方法 | 功能描述 |
---|---|
void setMaxInactiveInterval(int 秒) | 设置会话最大非活动时间时隔,单位是秒 |
session中的方法 | 说明 |
---|---|
int getMaxInactiveInterval() | 得到服务器上会话最大的非活动时间间隔,默认是1800秒(30分钟) |
@WebServlet("/sessionDestroy01Servlet")
public class SessionDestroy01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建session
HttpSession session = request.getSession();
System.out.println("更改session的存活时间前="+session.getId());
//1.int getMaxInactiveInterval()得到服务器上会话最大的非活动时间间隔,默认是1800秒(30分钟)
int time = session.getMaxInactiveInterval();
System.out.println("time = " + time);
//2.void setMaxInactiveInterval(int 秒)设置会话最大非活动时间时隔,单位是秒
// session.setMaxInactiveInterval(5);
//负数时间指示会话永远不会超时。
session.setMaxInactiveInterval(-1);
System.out.println("更改session的存活时间后="+session.getId());
int time2 = session.getMaxInactiveInterval();
System.out.println("time = " + time2);
}
}
7、session持久化方案(重要)
【1】问题引入:
tomcat在创建cookie的时候属于会话级别的cookie,关闭浏览器,cookie消失,
下次打开浏览器不会携带之前的cookie即cookie中的JSESSIONID到tomcat服务器中了,
那么这样会造成tomcat服务器中会有很多个不能使用的session容器(session依然还在,只是找不到了)。
严重的话会造成服务器宕机。
@WebServlet("/sessionPersis01Servlet")
public class SessionPersis01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取session
HttpSession session = request.getSession();
System.out.println(session.getId());
}
}
【2】解决方案
持久化session解决问题
主要问题是cookie是会话级别的,我们只需要将会话级别的cookie变为持久化级别的即可。
持久化session就是持久化cookie
步骤:
1.创建session
2.获取session的JSESSIOID的值
3.创建Cookie ,Cookie(“JSESSIOID”,值)
4.使用cookie对象调用方法setMaxAge()进行cookie的持久化,存活时间建议30min
5.将cookie响应给浏览器
@WebServlet("/sessionPersis01Servlet")
public class SessionPersis01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建session
HttpSession session = request.getSession();
//2.获取session的JSESSIOID的值
String sessionId = session.getId();
System.out.println(sessionId);
//3.创建Cookie ,Cookie("JSESSIOID",值)
Cookie cookie = new Cookie("JSESSIONID", sessionId);
//4.使用cookie对象调用方法setMaxAge()进行cookie的持久化,存活时间建议30min
cookie.setMaxAge(60*30);
//5.将cookie响应给浏览器
response.addCookie(cookie);
}
}
8、Session的钝化与活化技术(了解) 面试
钝化:就是正常关闭tomcat服务器,会将session容器中的数据长久保存到硬盘上。底层原理是序列化。
活化:就是启动tomcat服务器,将之前钝化的session容器读取到内存中。底层原理是反序列化。
注意:
1.由于钝化和活化的原理是序列化和反序列,所以要求存储在session容器中的对象所属类必须实现序列化接口Serializable。
2.演示钝化和活化效果不能在idea中演示,我们需要将当前项目打成war包放到tomcat服务器中的webapps目录下进行演示。
3.钝化:就是正常关闭tomcat服务器,将session中的的数据长久保存到硬盘上,会在work\Catalina\localhost\项目名下生成一个钝化文件。该文件中保存的就是session中的数据。实现原理是序列化。
4.活化:启动tomcat服务器将之前钝化的文件读取内存中,文件会自动消失。原理是反序列化。
钝化和活化的演示
【1】代码:
@WebServlet("/setSessionServlet")
public class SetSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.创建session
HttpSession session = request.getSession();
//2.获取session的id
String sessionId = session.getId();
//3.创建商品对象
Product p = new Product("笔记本", 9999);
//4.将商品对象存储到session中
session.setAttribute("p",p);
//5.响应数据
response.getWriter().print("setSessionServlet.....当前JSESSIONID="+sessionId);
}
}
@WebServlet("/getSessionServlet")
public class GetSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取session
HttpSession session = request.getSession();
//2.获取session的id
String sessionId = session.getId();
//3.从session中取出商品
Product p = (Product) session.getAttribute("p");
//4.响应数据
response.getWriter().print("getSessionServlet.....当前JSESSIONID="+sessionId+",p="+p.toString());
}
}
【2】打war包
6.构建完成之后:
7.将上述生成的war包复制到tomcat的webapps目录下:
8.到tomcat的bin目录下启动tomcat
注意:这里启动一定将idea中的tomcat关闭。
9.在浏览器中访问servlet
10.正常关闭tomcat:bin目录下面的shutdown.bat
11.在如下目录生成session的钝化文件:
12.正常启动tomcat
钝化文件就会被加载到内存,文件自动消失
9、Cookie禁用后Session的处理【了解】
1.如果cookie被禁用,如何找到对应的session?
重写url,有2种方式:
1)重定向重写url:
String url = response.encodeRedirectURL(path);
2)超链接重写url
String url = response.encodeURL(path);
核心思想都是在url后面拼接当前session的JSESSIONID.
/demo02;jsessionid=0073C2468966D58AF52BBC728A688577
2.注意两种方式区别:
超链接重写url:如果url是空字符串,那么也会在url后面拼接JSESSIONID.
重定向重写url: 如果url是空字符串,不做拼接
1.问题
cookie禁用后,浏览器中不能保存cookie了,那么没有cookie,就没有JSESSIONID,
那么这样就找不到tomcat服务器中的session容器了,
这样会造成tomcat服务器中的存在过多的不能使用的session容器。
2.解决问题
重写url即在url后面拼接当前session的唯一标识JSESSIONID.
3.两种方法
重定向重写URL
方法 | 说明 |
---|---|
response.encodeRedirectURL(path) | 将重定向跳转的地址进行重写,添加一个会话ID在后面。 |
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
重定向重写URL 方式解决禁用cookie访问session的问题:
response.encodeRedirectURL(path)将重定向跳转的地址进行重写,添加一个会话ID在后面。
*/
//1.创建session
HttpSession session = request.getSession();
//2.输出id
System.out.println(session.getId());
//3.重定向到 /demo02
String path = "/demo02";
// response.encodeRedirectURL(path)将重定向跳转的地址进行重写,添加一个会话ID在后面。
///demo02;jsessionid=0073C2468966D58AF52BBC728A688577
String url = response.encodeRedirectURL(path);
response.sendRedirect(url);
}
}
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建session
HttpSession session = request.getSession();
//2.输出id
System.out.println(session.getId());
}
}
超连接重写URL
方法 | 说明 |
---|---|
response.encodeURL(path) | 对要跳转到的地址使用URL重写 |
@WebServlet("/demo03")
public class Demo03Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
/*
超连接重写URL 方式解决禁用cookie访问session的问题:
response.encodeURL(path)对要跳转到的地址使用URL重写
*/
//1.创建session
HttpSession session = request.getSession();
//2.输出id
System.out.println(session.getId());
//3.超连接重写URL /demo04
String path = "/demo04";
//4.响应给浏览器一个超链接
String url = response.encodeURL(path);
//<a href='/demo04'>超连接重写URL</a>
// response.getWriter().print("<a href='"+url+"'>超连接重写URL</a>");
response.getWriter().print("<a href=\""+url+"\">超连接重写URL</a>");
}
}
@WebServlet("/demo04")
public class Demo04Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建session
HttpSession session = request.getSession();
//2.输出id
System.out.println(session.getId();
}
}
10、servlet的三大域对象(重要)
- 掌握servlet的三大域对象特点和区别以及应用场景
【1】
域对象名 | 描述 |
---|---|
session—HttpSession接口 | 比request大,属于一次会话过程中使用的,可以实现多次请求来共享session域对象中的数据。应用场景:验证码 购物车 只有登录才可以操作一些需求等 |
request—HttpServletRequest接口 | 最小的域对象,只能在一次请求中使用,应用场景:请求转发携带request域对象数据。 |
servletContext—ServletContext接口 | 最大的域对象,表示当前项目的上下文对象,即当前项目。tomcat服务器一启动就会创建,当tomcat服务器关闭才会销毁。存活时间最长的。多次请求。应用场景:统计网站访问次数,读取当前项目的配置文件等 |
【2】
request < session < servletContext
【3】
【API操作】操作三个作用域对象的API
-
存储数据:setAttribute(name,value);
-
获得数据:getAttribute(name);
-
删除数据:removeAttribute(name);
11、案例【Session验证码登陆】【重要】
1.需求
验证码是由服务器给浏览器端生成的一张图片,这张图片上有扭曲变形的数字或文字。主要是用来防止恶意注册或者登陆的。
2.流程
3.实现步骤
1.创建登录页面login.html
2.定义一个CheckCodeServlet用来生成验证码图片
验证码校验整体思路:
1)页面一加载就向CheckCodeServlet发送请求获取验证码图片上的验证码
2)每次点击验证码图片重新向CheckCodeServlet发送请求获取验证码图片上的验证码
3)点击登录按钮将输入框输入的验证码和后台存储到session中的验证码进行比较
通过验证码案例我们知道session的应用场景,可以在多次请求中共享session中的数据
3.定义一个LoginServlet用来登录时校验验证码是否正确
4.代码实现
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h2>登录页面</h2>
<form action="/loginServlet" method="post">
账号<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
验证码<input type="text" name="code">
<!--页面一加载就向checkCodeServlet发送请求-->
<img src="/checkCodeServlet" alt="验证码" style="cursor: pointer" onclick="changeCheckCode(this);"><br>
<input type="submit" value="登录">
</form>
<script type="text/javascript">
//定义函数更改验证码图片
//obj 表示img标签对象
function changeCheckCode(obj) {//obj=this
/*
每次点击验证码图片重新向后台checkCodeServlet发送新的请求
问题:当我们点击验证码图片,发现浏览器并没有向后台发送请求,因为浏览器认为当前发送的请求的路径是同一个,那么浏览器直接将当前浏览器的图片
显示了
解决问题:
我们可以在请求的路径后面增加一个时间戳,告知浏览器这是一个新的请求,和原来的请求不一样
*/
// obj.src = "/checkCodeServlet";//同一个请求,不会发送新的请求
obj.src = "/checkCodeServlet?t="+new Date().getTime();//每次请求都不一样
}
</script>
</body>
</html>
CheckCodeServlet类
用来生成验证码图片
package com.itheima.sh.a_session_checkcode_01;
import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
/*
说明:验证码是一张图片,而这个图片中的内容是使用代码生成的。
分析和步骤:
1)创建一个可以存放图片的缓冲区BufferedImage作为画布;
2)通过画布获取到针对这个画布的画笔;
3)修改画布的背景颜色为白色;
4)设置画布的边框,画边框的时候需要注意下,如果这里写画布的宽width和高height ,就会超出画布就会看不见,所以width和height 分别-1;
5)创建一个获取随机数的对象;
6)给画布上写数据;
7)给画布上画干扰线;
8)需要把画布中的内容响应给浏览器;ImageIO.write(bi,"JPG",response.getOutputStream());
*/
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//定义画布的宽和高
int width = 120;
int height = 30;
//创建一个可以存放图片的缓冲区,作为画布
//BufferedImage.TYPE_INT_RGB 表示生成图片的类型
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//通过画布获取到针对这个画布的画笔
Graphics g = bi.getGraphics();
//修改画布的背景颜色 每次使用画笔的时候都得给画笔指定颜色
g.setColor(Color.WHITE);
//填充画布
g.fillRect(0, 0, width, height);
//设置画布的边框
//给画笔指定颜色
g.setColor(Color.RED);
//给画布画边框 如果这里写width height 就会超过画布,因为边框也会占一个像素,所以这里宽和高都需要-1
g.drawRect(0, 0, width - 1, height - 1);
//创建一个获取随机数的对象
Random r = new Random();
//给画布上画干扰线
//循环控制画多条线
for (int i = 1; i <= 3; i++) {
//设置画笔的颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
//向画布上画干扰线
//drawLine(x1, y1, x2, y2) 这里四个参数是因为两个点画成一条线
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
}
//定义数据准备向画布中写数据
String data = "abcdefghigklmnpqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ123456789";
//创建字符串缓冲区
StringBuilder sb = new StringBuilder();
//循环控制画四个字符
for (int i = 1; i <= 4; i++) {
//设置画笔的颜色 Color.BLUE这里的颜色固定了,只能是蓝色,我们可以让颜色随机变化
// g.setColor(Color.BLUE);
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
//设置字体 Font.ITALIC表示斜体
g.setFont(new Font("宋体", Font.ITALIC, 20));
//给画布上写内容 20表示从x轴的位置开始书写 25表示y轴位置开始书写
// g.drawString("哈哈哈哈", 20, 25);
/*
* data.charAt()表示根据函数的参数进行查找字符
* data.length()表示字符串的长度
* r.nextInt()表示生成随机数,但是随机数的范围在0~data字符串的长度
*/
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 20 * i, 25);
//将验证码内容拼接到字符串缓冲区中
sb.append(str);
}
// 验证码保存到session中
request.getSession().setAttribute("checkcode", sb.toString());
//将生成的验证码图片响应给浏览器
ImageIO.write(bi, "JPG", response.getOutputStream());
}
}
LoginServlet类
登录时校验验证码是否正确
package com.itheima.sh.a_session_checkcode_01;
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;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.处理post请求乱码
request.setCharacterEncoding("utf-8");
//2.获取用户名和密码以及输入框的验证码
String username = request.getParameter("username");
String password = request.getParameter("password");
//页面的输入框的验证码
String input_code = request.getParameter("code");
//3.从session中获取验证码图片
// request.getSession().setAttribute("checkcode", sb.toString());
String session_checkcode = (String) request.getSession().getAttribute("checkcode");
//4.比较从session中获取的验证码和从页面获取的验证码是否相等:
if(input_code!=null && input_code.equalsIgnoreCase(session_checkcode)){
//相等:将用户名和密码传递到dao层向数据库查询
System.out.println("验证码校验通过");
}else{
//不相等:响应给浏览器客户端提示验证码输入有误
System.out.println("验证码校验没有通过");
}
}
}