HttpSession概述
什么是HttpSesssion
javax.servlet.http.HttpSession接口表示一個(gè)會(huì)話,我們可以把一個(gè)會(huì)話內(nèi)需要共享的數(shù)據(jù)保存到HttSession對(duì)象中伟桅。
獲取HttpSession對(duì)象
HttpSession request.getSesssion():如果當(dāng)前會(huì)話已經(jīng)有了session對(duì)象那么直接返回伍俘,如果當(dāng)前會(huì)話還不存在會(huì)話,那么創(chuàng)建session并返回蚣抗;
HttpSession request.getSession(boolean):當(dāng)參數(shù)為true時(shí)侈百,與requeset.getSession()相同。如果參數(shù)為false翰铡,那么如果當(dāng)前會(huì)話中存在session則返回钝域,不存在返回null;
HttpSession是域?qū)ο?/h3>
我們已經(jīng)學(xué)習(xí)過(guò)HttpServletRequest锭魔、ServletContext例证,它們都是域?qū)ο螅F(xiàn)在我們又學(xué)習(xí)了一個(gè)HttpSession迷捧,它也是域?qū)ο笾帧K鼈內(nèi)齻€(gè)是Servlet中可以使用的域?qū)ο螅鳭SP中可以多使用一個(gè)域?qū)ο竽铮魈煳覀冊(cè)僦v解JSP的第四個(gè)域?qū)ο蟆?br>
HttpServletRequest:一個(gè)請(qǐng)求創(chuàng)建一個(gè)request對(duì)象笙蒙,所以在同一個(gè)請(qǐng)求中可以共享request,例如一個(gè)請(qǐng)求從AServlet轉(zhuǎn)發(fā)到BServlet庆锦,那么AServlet和BServlet可以共享request域中的數(shù)據(jù)捅位;
ServletContext:一個(gè)應(yīng)用只創(chuàng)建一個(gè)ServletContext對(duì)象,所以在ServletContext中的數(shù)據(jù)可以在整個(gè)應(yīng)用中共享,只要不啟動(dòng)服務(wù)器艇搀,那么ServletContext中的數(shù)據(jù)就可以共享尿扯;
HttpSession:一個(gè)會(huì)話創(chuàng)建一個(gè)HttpSession對(duì)象,同一會(huì)話中的多個(gè)請(qǐng)求中可以共享session中的數(shù)據(jù)焰雕;
下面是session的域方法:
void setAttribute(String name, Object value):用來(lái)存儲(chǔ)一個(gè)對(duì)象姜胖,也可以稱之為存儲(chǔ)一個(gè)域?qū)傩裕纾簊ession.setAttribute(“xxx”, “XXX”)淀散,在session中保存了一個(gè)域?qū)傩杂依常驅(qū)傩悦Q為xxx,域?qū)傩缘闹禐閄XX档插。請(qǐng)注意慢蜓,如果多次調(diào)用該方法,并且使用相同的name郭膛,那么會(huì)覆蓋上一次的值晨抡,這一特性與Map相同;
Object getAttribute(String name):用來(lái)獲取session中的數(shù)據(jù)则剃,當(dāng)前在獲取之前需要先去存儲(chǔ)才行耘柱,例如:String value = (String) session.getAttribute(“xxx”);,獲取名為xxx的域?qū)傩裕?br>
void removeAttribute(String name):用來(lái)移除HttpSession中的域?qū)傩怨飨郑绻麉?shù)name指定的域?qū)傩圆淮嬖诘骷澹敲幢痉椒ㄊ裁炊疾蛔觯?br>
Enumeration getAttributeNames():獲取所有域?qū)傩缘拿Q;
登錄案例
需要的頁(yè)面:
login.jsp:登錄頁(yè)面己肮,提供登錄表單士袄;
index1.jsp:主頁(yè),顯示當(dāng)前用戶名稱谎僻,如果沒(méi)有登錄娄柳,顯示您還沒(méi)登錄;
index2.jsp:主頁(yè)艘绍,顯示當(dāng)前用戶名稱赤拒,如果沒(méi)有登錄,顯示您還沒(méi)登錄诱鞠;
Servlet:
LoginServlet:在login.jsp頁(yè)面提交表單時(shí)挎挖,請(qǐng)求本Servlet。在本Servlet中獲取用戶名般甲、密碼進(jìn)行校驗(yàn)肋乍,如果用戶名、密碼錯(cuò)誤敷存,顯示“用戶名或密碼錯(cuò)誤”,如果正確保存用戶名session中,然后重定向到index1.jsp锚烦;
當(dāng)用戶沒(méi)有登錄時(shí)訪問(wèn)index1.jsp或index2.jsp觅闽,顯示“您還沒(méi)有登錄”。如果用戶在login.jsp登錄成功后到達(dá)index1.jsp頁(yè)面會(huì)顯示當(dāng)前用戶名涮俄,而且不用再次登錄去訪問(wèn)index2.jsp也會(huì)顯示用戶名蛉拙。因?yàn)槎啻握?qǐng)求在一個(gè)會(huì)話范圍,index1.jsp和index2.jsp都會(huì)到session中獲取用戶名彻亲,session對(duì)象在一個(gè)會(huì)話中是相同的孕锄,所以都可以獲取到用戶名!
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>login.jsp</title>
</head>
<body>
<h1>login.jsp</h1>
<hr/>
<form action="/day06_4/LoginServlet" method="post">
用戶名:<input type="text" name="username" /><br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
index1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>index1.jsp</title>
</head>
<body>
<h1>index1.jsp</h1>
<%
String username = (String)session.getAttribute("username");
if(username == null) {
out.print("您還沒(méi)有登錄苞尝!");
} else {
out.print("用戶名:" + username);
}
%>
<hr/>
<a href="/day06_4/index2.jsp">index2</a>
</body>
</html>
index2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>index2.jsp</title>
</head>
<body>
<h1>index2.jsp</h1>
<%
String username = (String)session.getAttribute("username");
if(username == null) {
out.print("您還沒(méi)有登錄畸肆!");
} else {
out.print("用戶名:" + username);
}
%>
<hr/>
<a href="/day06_4/index1.jsp">index1</a>
</body>
</html>
LoginServlet
public class LoginServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
if(username.equalsIgnoreCase("cloud")) {
response.getWriter().print("用戶名或密碼錯(cuò)誤!");
} else {
HttpSession session = request.getSession();
session.setAttribute("username", username);
response.sendRedirect("/day06_4/index1.jsp");
}
}
}
session的實(shí)現(xiàn)原理
session底層是依賴Cookie的宙址!我們來(lái)理解一下session的原理吧轴脐!
當(dāng)我首次去銀行時(shí),因?yàn)檫€沒(méi)有賬號(hào)抡砂,所以需要開一個(gè)賬號(hào)大咱,我獲得的是銀行卡,而銀行這邊的數(shù)據(jù)庫(kù)中留下了我的賬號(hào)注益,我的錢是保存在銀行的賬號(hào)中碴巾,而我?guī)ё叩氖俏业目ㄌ?hào)。
當(dāng)我再次去銀行時(shí)丑搔,只需要帶上我的卡餐抢,而無(wú)需再次開一個(gè)賬號(hào)了。只要帶上我的卡低匙,那么我在銀行操作的一定是我的賬號(hào)旷痕!
當(dāng)首次使用session時(shí),服務(wù)器端要?jiǎng)?chuàng)建session顽冶,session是保存在服務(wù)器端欺抗,而給客戶端的session的id(一個(gè)cookie中保存了sessionId)∏恐兀客戶端帶走的是sessionId绞呈,而數(shù)據(jù)是保存在session中。
當(dāng)客戶端再次訪問(wèn)服務(wù)器時(shí)间景,在請(qǐng)求中會(huì)帶上sessionId佃声,而服務(wù)器會(huì)通過(guò)sessionId找到對(duì)應(yīng)的session,而無(wú)需再創(chuàng)建新的session倘要。
session與瀏覽器
session保存在服務(wù)器圾亏,而sessionId通過(guò)Cookie發(fā)送給客戶端十拣,但這個(gè)Cookie的生命不-1,即只在瀏覽器內(nèi)存中存在志鹃,也就是說(shuō)如果用戶關(guān)閉了瀏覽器夭问,那么這個(gè)Cookie就丟失了。
當(dāng)用戶再次打開瀏覽器訪問(wèn)服務(wù)器時(shí)曹铃,就不會(huì)有sessionId發(fā)送給服務(wù)器缰趋,那么服務(wù)器會(huì)認(rèn)為你沒(méi)有session,所以服務(wù)器會(huì)創(chuàng)建一個(gè)session陕见,并在響應(yīng)中把sessionId中到Cookie中發(fā)送給客戶端秘血。
你可能會(huì)說(shuō)评甜,那原來(lái)的session對(duì)象會(huì)怎樣灰粮?當(dāng)一個(gè)session長(zhǎng)時(shí)間沒(méi)人使用的話,服務(wù)器會(huì)把session刪除了蜕着!這個(gè)時(shí)長(zhǎng)在Tomcat中配置是30分鐘谋竖,可以在${CATALANA}/conf/web.xml找到這個(gè)配置,當(dāng)然你也可以在自己的web.xml中覆蓋這個(gè)配置承匣!
web.xml
<session-config>
<session-timeout>30</session-timeout>
</session-config>
session失效時(shí)間也說(shuō)明一個(gè)問(wèn)題蓖乘!如果你打開網(wǎng)站的一個(gè)頁(yè)面開始長(zhǎng)時(shí)間不動(dòng),超出了30分鐘后韧骗,再去點(diǎn)擊鏈接或提交表單時(shí)你會(huì)發(fā)現(xiàn)嘉抒,你的session已經(jīng)丟失了!
session其他常用API
String getId():獲取sessionId袍暴;
int getMaxInactiveInterval():獲取session可以的最大不活動(dòng)時(shí)間(秒)些侍,默認(rèn)為30分鐘。當(dāng)session在30分鐘內(nèi)沒(méi)有使用政模,那么Tomcat會(huì)在session池中移除這個(gè)session岗宣;
void setMaxInactiveInterval(int interval):設(shè)置session允許的最大不活動(dòng)時(shí)間(秒),如果設(shè)置為1秒淋样,那么只要session在1秒內(nèi)不被使用耗式,那么session就會(huì)被移除;
long getCreationTime():返回session的創(chuàng)建時(shí)間趁猴,返回值為當(dāng)前時(shí)間的毫秒值刊咳;
long getLastAccessedTime():返回session的最后活動(dòng)時(shí)間,返回值為當(dāng)前時(shí)間的毫秒值儡司;
void invalidate():讓session失效娱挨!調(diào)用這個(gè)方法會(huì)被session失效,當(dāng)session失效后捕犬,客戶端再次請(qǐng)求跷坝,服務(wù)器會(huì)給客戶端創(chuàng)建一個(gè)新的session酵镜,并在響應(yīng)中給客戶端新session的sessionId;
boolean isNew():查看session是否為新探孝。當(dāng)客戶端第一次請(qǐng)求時(shí)笋婿,服務(wù)器為客戶端創(chuàng)建session誉裆,但這時(shí)服務(wù)器還沒(méi)有響應(yīng)客戶端顿颅,也就是還沒(méi)有把sessionId響應(yīng)給客戶端時(shí),這時(shí)session的狀態(tài)為新足丢。
URL重寫
我們知道session依賴Cookie粱腻,那么session為什么依賴Cookie呢?因?yàn)榉?wù)器需要在每次請(qǐng)求中獲取sessionId斩跌,然后找到客戶端的session對(duì)象绍些。那么如果客戶端瀏覽器關(guān)閉了Cookie呢?那么session是不是就會(huì)不存在了呢耀鸦?
其實(shí)還有一種方法讓服務(wù)器收到的每個(gè)請(qǐng)求中都帶有sessioinId柬批,那就是URL重寫!在每個(gè)頁(yè)面中的每個(gè)鏈接和表單中都添加名為jSessionId的參數(shù)袖订,值為當(dāng)前sessionid氮帐。當(dāng)用戶點(diǎn)擊鏈接或提交表單時(shí)也服務(wù)器可以通過(guò)獲取jSessionId這個(gè)參數(shù)來(lái)得到客戶端的sessionId,找到sessoin對(duì)象洛姑。
index.jsp
<body>
<h1>URL重寫</h1>
<a href='/day06_5/index.jsp;jsessionid=<%=session.getId() %>' >主頁(yè)</a>
<form action='/day06_5/index.jsp;jsessionid=<%=session.getId() %>' method="post">
<input type="submit" value="提交"/>
</form>
</body>
也可以使用response.encodeURL()對(duì)每個(gè)請(qǐng)求的URL處理上沐,這個(gè)方法會(huì)自動(dòng)追加jsessionid參數(shù),與上面我們手動(dòng)添加是一樣的效果楞艾。
<a href='<%=response.encodeURL("/day06_5/index.jsp") %>' >主頁(yè)</a>
<form action='<%=response.encodeURL("/day06_5/index.jsp") %>' method="post">
<input type="submit" value="提交"/>
</form>
使用response.encodeURL()更加“智能”参咙,它會(huì)判斷客戶端瀏覽器是否禁用了Cookie,如果禁用了硫眯,那么這個(gè)方法在URL后面追加jsessionid蕴侧,否則不會(huì)追加。