一、會話技術
1. 什么是會話
在計算機術語中,會話指的是客戶端和服務器交互通訊的過程。簡單的理解,大家可以看成是兩個普通的人在打電話。一次電話從通話開始到掛斷,可以看成是會話。
- 會話的特征
- 會話能夠把同一用戶發出的不同請求之間關聯起來。不同用戶的會話應當是相互獨立的
- 會話一旦建立就一直存在,直到用戶空閑時間超過了某一個時間界限,會話才會中斷。 好比大家打電話,超過了某一定時間就會自動斷開。
2. 什么是會話技術
HTTP是一種無狀態協議,每當用戶發出請求時,服務器就會做出響應,客戶端與服務器之間的聯系是離散的、非連續的。當用戶在同一網站的多個頁面之間轉換時,根本無法確定是否是同一個客戶。
舉例如下:
- 我們在電商網站上選購商品,即便沒有登錄,假如購物車。下一次我們再打開網站,還是能夠看到上一次添加到購物車的商品
- 我們在一些網站登錄了之后,過了幾天我們在打開這個網站,還能認識我們,還能在右上角顯示
歡迎您 xxx
- 當我們再次登錄一些論壇時,總會顯示上一次我們訪問論壇的時間。
3. 會話技術的分類
會話技術主要常用的有兩種, 一種是
Cookie
, 另一種是Session
.
二、 Cookie
1. 什么是cookie
Cookie 可以翻譯為“小甜品,小餅干” , Cookie 實際上是指小量信息,是由 Web 服務器創建的,將信息存儲在用戶計算機上的文件。當用戶通過瀏覽器訪問服務器的時候,瀏覽器會自動攜帶早前存儲的cookie ,傳遞給服務器。
2. Cookie的應用場景
所有服務器想要讓客戶端幫忙存儲一些數據,以便下次訪問能夠識別客戶的情形,都可以使用cookie,如:
-
商品瀏覽記錄
-
購物車信息
-
記住賬號密碼
...
3. Cookie的基本使用
大家要記住一個核心,cookie是服務器生成,然后寄存在客戶端的一小份數據。 那么服務器如何把這一小份數據給客戶端呢? 只能依靠前面講過的
response
對象了。
Cookie cookie = new Cookie("key" ,"value");
response.addCookie(cookie);
4. Cookie的分類
cookie有分臨時性(也稱之為會話級別cookie),也有持久性。
- 臨時性Cookie
默認情況下,關閉瀏覽器后,cookie就銷毀掉了。
- 持久性Cookie
可以設置保存時長
5. 獲取上次訪問時間
- 頁面准備
<form action="user_login" method="post">
用戶名:<input type="text" name="username"/></br>
密 碼:<input type="password" name="password"/></br>
<input type="submit" value="登錄"/>
</form>
- controller
@RestController
public class UserController {
private static final String TAG = "UserController";
@RequestMapping("user_login")
public String login(HttpServletRequest request , HttpServletResponse response , String username , String password){
System.out.println(username + " : " + password);
HttpSession session = request.getSession();
// session.setAttribute("name","zhangsan");
String id = session.getId();
System.out.println("id=" + id);
//1. 判斷賬號密碼
if("admin".equals(username) && "123".equals(password)){
String result = "歡迎您 , "+ username;
//2.要獲取cookies
Cookie[] cookies = request.getCookies();
//有cookie
if(cookies != null){
//就想看cookie里面有沒有帶上次的時間
for(Cookie cookie : cookies){
//如果能成立,就表示找到了上一次訪問的時間。
if("last".equals(cookie.getName())){
long time = Long.parseLong(cookie.getValue());
Date date = new Date();
date.setTime(time);
result += "\r\n 上一次訪問時間是:"+date.toLocaleString();
}
}
}
//只要有登錄成功,就必須返回最新的登錄時間。
String time = System.currentTimeMillis()+"";
Cookie cookie = new Cookie("last" , time);
cookie.setMaxAge(60*60*24*7);
response.addCookie(cookie);
return result;
}
return "登錄失敗";
}
}
6. Cookie的問題
- cookie 大小有限制,每個服務器在最多不能超過20個。
- cookie 是存放在客戶端上,信息不安全,有被截獲的可能
三、 Session
1. 什么是session
Session是基於Cookie的一種會話機制。 Session服務器上的一段內存空間 , 可以用來存儲數據。
Cookie是服務器返回一小份數據給客戶端,並且存放在客戶端上。 Session是,數據存放在服務器端。
2. Cookie 和 Session的對比
cookie
數據存放在客戶端
數據不安全
減輕服務器壓力, 用戶磁盤占用比較多。
存放的數據有限
session
數據存放在服務器端
數據相對比較安全。
服務器壓力大一點。
存放的數據 依賴服務器的內存空間。
3. Session的簡單使用
-
獲取session
HttpSession session = request.getSession()
-
存值
session.setAttribute(name ,value);
-
取值:
session.getAttribute(name);
-
移除值
session.removeAttribute(name);
-
讓session失效 作廢
session.invalidate();
-
獲取id值
session的id值就是這一塊內存空間的唯一標識符。 session.getId() .
4. Session的背后細節
- 第一次訪問
那么session都沒有創建,並且咱們的請求對象里面也不帶任何的cookie過來。 那么這個時候會在內存中給你創建一個新的session區域,並且把這個session的id值放到cookie里面給我們的瀏覽器返回。 注意這些事情都是服務器端自己做的。所以cookie是一個臨時性的cookie。 關閉瀏覽器就銷毀cookie了
- 第二次訪問
如果是第二次訪問,那么瀏覽器會把之前的那個cookie給帶過來, 服務器收到了cookie,里面有我們上一次給的sessionid 值, 那么這個時候再調用request.getSesion() , 它先會拿我們待過來的id ,到內存里面去找有沒有session的 id值跟這個cookie帶過來的一樣 ,如果有,就直接返回這個內存空間, 否則就創建新的session空間
5. session 銷毀的細節!
- 關閉瀏覽器並不能讓session銷毀。 因為session是服務器的一塊內存空間, 它的銷毀不依賴客戶端的瀏覽器關閉還是不關閉。
- 再次訪問的時候,我們的瀏覽器已經沒有了那個sessionid值,所以就找不到了以前的那一塊空間。所以打開瀏覽器重新訪問,會拿到的是全新的session空間。 但是原來的那個session還存在,只不過我們無法操作它而已。
- 如果真的項操作以前的session,我們要手動往cookie里面存session id 值,並且設置成持久性cookie
注意: cookie的那個name不能亂寫。 JSESSIONID
- 銷毀session
- invaidate()
- 自動失效。 有效期。 默認30分鍾
6. Session的作用范圍 & 生命周期
-
作用范圍
sesison 作用范圍: 一次會話范圍內有效。
-
生命周期
何時創建
第一次調用request.getSession()就創建。
何時銷毀
關閉服務器 session超時了。默認30分鍾。 從最后一次請求開始計時。 調用invalidate . 讓空間失效、作廢
7. 購物車案例
日常生活中,大家都是有過購物的經驗,喜歡什么商品,先把它添加到購物車中,后續再付款。 那么購物車是如何實現的呢? 其實購物車可以使用cookie來實現,也可以使用session來實現。真正的企業的購物車要實現起來還得配合緩存數據庫來實現。咱們接下來使用session來實現一個簡易的購物車即可
- 准備工作
需要在項目的build.gradle 中添加 thymeleaf依賴
compile("org.thymeleaf:thymeleaf-spring4:2.1.4.RELEASE")
- 在模板頁面上,要記得引入命名空間
<html xmlns:th="http://www.thymeleaf.org" >
1. 顯示商品列表
- index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2><a href="list">開始購物</a></h2>
</body>
</html>
- 商品實體類
public class Product {
private int id; // 商品的編號
private String name; //商品名字
private double price ; //商品價格
get & set 方法
...
}
- 商品controller
@Controller
public class ProductController {
private static final String TAG = "ProductController";
//准備商品數據
private static List<Product> list;
static{
list = new ArrayList<Product>();
list.add(new Product(1,"iphone6s" ,3788));
list.add(new Product(2,"小米8" ,2788));
list.add(new Product(3,"vivo20" ,4788));
list.add(new Product(4,"meizu6" ,3690));
list.add(new Product(5,"諾基亞200" ,188));
}
@RequestMapping("list")
public ModelAndView list(){
//1. 構建商品集合
//2. 存儲數據到model
ModelAndView modelAndView =new ModelAndView();
//設置模型數據
modelAndView.addObject("list" , list);
//設置跳轉的目標是哪里。 list.html
modelAndView.setViewName("list");
return modelAndView;
}
}
- 商品列表 list.html 位於 /resources/templates/下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8"/>
<title>商品列表頁面</title>
</head>
<body>
<h2>商品列表</h2><br/>
<table>
<thead>
<td width="100px">編號</td>
<td width="100px">名稱</td>
<td width="100px">價格</td>
<td width="100px">操作</td>
</thead>
<!--從這里開始就是真正的商品數據顯示了
p 遍歷 list集合的出來的每一個元素對象, 其實好就是商品對象。
aa: 表示遍歷的狀態信息
${aa.index} :遍歷的索引
-->
<tr th:each="p,status:${list}">
<td th:text="${p.id}">100</td>
<td th:text="${p.name}">蘋果筆記本</td>
<td th:text="${p.price}">9999</td>
<!--<td ><a href="addToCart?index=1">加入購物車</a></td>-->
<td ><a th:href="@{addToCart(index=${status.index})}">加入購物車</a></td>
</tr>
</table>
</body>
</html>
2. 加入購物車
點擊加入購物車需要跳轉到controller , 根據提供的索引下標,獲取到對應的商品,添加到購物車
Map
中
- 添加購物車方法
//添加商品到購物車, 現在拿到的是商品的索引
@RequestMapping("addToCart")
public String addToCart(HttpServletRequest request , int index){
System.out.println("要添加商品到購物車了~");
HttpSession session = request.getSession();
//1. 根據索引,獲取到要添加到購物車的商品
Product product = list.get(index);
//2. 加入購物車 購物車其實就是商品和數量的對應關系。
Map<Product , Integer> map = (Map<Product , Integer>)session.getAttribute("cartMap");
//第一次來,連購物車都沒有
if(map == null){
map = new HashMap<Product , Integer>();
}
//判斷購物車里面是否有該商品 ,如果有 數量 + 1 ,如果沒有,數量-1
if(map.containsKey(product)){
map.put(product , map.get(product) +1 );
}else{
map.put(product , 1 );
}
//3. 存儲到session中。
request.getSession().setAttribute("cartMap" , map);
//當前的跳轉是基於template/list.html ,所以需要重定向跳轉到中轉頁面
return "redirect:transfer.html";
}
- 中轉頁面
該頁面只是一個簡單的靜態頁面,所以位於 resource/static下
<body>
<a href="list"><h2>繼續購物</h2></a><br>
<a href="toCart"><h2>去購物車查看</h2></a><br>
</body>
3. 顯示購物車
通過點擊去購物車查看, 即可跳轉到購物車頁面,顯示商品。
- cart.html
該頁面是用於顯示動態數據,所以需要放到模板 templates下。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>購物車列表</h2>
<table>
<thead>
<td width="100px">商品名稱</td>
<td width="100px">商品價格</td>
<td width="100px">購買數量</td>
<td width="100px">小計</td>
</thead>
<!--把session里面的購物車拿出來遍歷顯示。 購物車是一個map集合
map集合的遍歷出來,其實就是一個key 和 一個value 的組合 。
這種組合entry
m就是entry
map<Product , Integer>
-->
<tbody>
<tr th:each="m:${session.cartMap}">
<td th:text="${m.key.name}">聯想鼠標墊</td>
<td th:text="${m.key.price}">10.0</td>
<td th:text="${m.value}">1</td>
<td th:text="${m.key.price * m.value}">1</td>
</tr>
</tbody>
</table>
</body>
</html>