HTTP 协议使用的是无状态连接. 客户浏览器与服务器建立连接, 发出请求, 得到响应, 然后关闭连接. 换句话说, 连接只为一个请求 / 响应而存在.
由于连接不会持久保留, 所以容器认不出做第二个请求的客户与前一个请求的客户是同一个客户. 对于容器而言, 每一个请求都来自一个新的客户.
这时候, 就需要用到会话(session).
1, 什么是会话?
下面是维基百科的解释:
In computer science, in particular networking, a session is a temporary and interactive information interchange between two or more communicating devices, or between a computer and user (see login session). A session is established at a certain point in time, and then 'torn down' - brought to an end - at some later point. An established communication session may involve more than one message in each direction. A session is typically stateful, meaning that at least one of the communicating parties needs to hold current state information and save information about the session history in order to be able to communicate, as opposed to stateless communication, where the communication consists of independent requests with responses.
在计算机网络中, 会话是两个或者多个通讯设备之间或者人和电脑之间临时的信息交换. 会话在某个时间点创建, 在其后的某个时间点被销毁. 一个已创建的会话一般在每个方向上都包括一条或者多条消息. 和无状态的通讯中只包含相互独立的请求响应不同, 会话是有状态的, 也就是说, 参与通讯的各方都要保留关于会话历史的状态信息, 以便能够相互通信.
2,Java web 开发中会话的使用
在 Web 开发中, 服务器可以为每个用户浏览器创建一个会话对象(session 对象), 一个浏览器独占一个 session 对象(默认情况). 因此, 在需要保存用户数据时, 服务器程序可以把用户数据写到用户浏览器独占的 session 中, 当用户使用浏览器访问其它程序时, 其它程序可以从用户的 session 中取出该用户的数据, 为用户服务.
Java Web 开发中, 容器以某种方式吧会话 ID 作为响应的一部分交给客户, 而客户把会话 ID 作为请求的一部分发回. 最简单常用的方式是通过 cookie 交换这个会话 ID 信息.
2.1 创建一个会话和从请求中得到一个会话
2.1.1 创建一个会话
在 Java Web 开发中, 创建一个会话, 只需要用:
HttpSession session = request.getSession();
通过这种方式, 在服务方法中请求一个会话, 容器会自动完成具体的工作, 主要包括:
创建一个新的 HttpSession 对象.
生成唯一的会话 ID.
建立新的 Cookie 对象.
将会话 ID 和 Cookie 关联.
在响应中设置 Cookie(设置 Http 响应的 Set-Cookie 首部).
2.1.2 从请求中得到一个会话
浏览器在发送请求时, 会自动将 Cookie 中的信息, 放在 http 请求的首部, 上送到服务器端. 这样, 服务器端就能够拿到之前写在 Cookie 中的会话 ID, 然后, 利用该会话 ID, 可以得到会话对象. 而这些操作, 容器中也有封装的实现, 编程中只需要调用即可.
实际上, 从请求中得到会话, 也是用上面的方法:
HttpSession session = request.getSession();
该方法会判断当前请求是否包含一个会话 ID cookie: 如果包含, 找到与该 ID 匹配的会话; 如果不包含会话 ID cookie 或者没有与该会话 ID 匹配的当前会话, 就会创建一个新的会话对象.
2.2 如何判断会话是否是新创建的
从上面可以看到, 创建一个会话和从请求获得一个会话的方式是一样的. 那么, 怎么判断会话是刚刚创建的, 还是已经存在的呢? Session 对象有一个 isNew()方法, 下面是一个例子.
- package com.Web.test;
- import javax.servlet.ServletException;
- 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;
- /**
- * Created by chengxia on 2018/12/3.
- */
- public class ServletCookieTest extends HttpServlet {
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html");
- PrintWriter out = response.getWriter();
- out.print("Test Session isNew().<br/>");
- HttpSession session = request.getSession();
- if(session.isNew()){
- out.println("This is a new session. Is this your first time here?");
- }else{
- out.println("Welcome Back!");
- }
- out.flush();
- out.close();
- }
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- this.doPost(request, response);
- }
- }
这样, 在浏览器中, 第一次访问这个 Servlet 的时候, 就会看到:
First Access
说明当前会话是新创建的. 而第二次访问该 servlet 的时候, 就会看到:
Second Access
同时, 在上面第二个图中, 可以看到, 浏览器地址栏左方有一个圆圈 i 的图标, 点击这个图标可以查看网站信息. 从弹出的对话框中, 可以看到已经确实会话 ID 信息已经写入了浏览器 Cookie. 如下图:
Cookie Set
有时候, 如果只是想得到一个已有的会话, 只在没有 request.getSession()方法的时候, 传入一个布尔型的参数, 该参数用于标识在没有已存在会话的前提下是否要创建新会话对象, false 表示不会创建新会话对象, 这时候, 该方法就会返回 null. 而传入 true 参数的情况, 就和缺省的没有参数的情况一样.
2.3 浏览器禁用 Cookie 时的会话处理机制
如果浏览器禁用了 cookie, 那前面提到的通过 Cookie 来传递会话 ID 的方法就不好用了. 这时候, 就需要用到 url 重写.
简单的说, 就是在请求的 URL 中通过参数将会话 ID 传递到服务器端. 这样, 服务端的程序就能够根据传过来的会话 ID, 获得会话对象. 当然, 这些操作, 在容器的机制中, 都有封装好的实现, 只需要调用即可. 下面是一个例子:
- package com.Web.test;
- import javax.servlet.ServletException;
- 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;
- /**
- * Created by chengxia on 2018/12/3.
- */
- public class ServletUrlEncodeTest extends HttpServlet {
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html");
- PrintWriter out = response.getWriter();
- HttpSession session = request.getSession();
- out.print("<html><body>");
- out.print("<a href=\"" + response.encodeURL("/ServletUrlEncode.Test") + "\">Test Link</a>");
- out.print("</body></html>");
- out.flush();
- out.close();
- }
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- this.doPost(request, response);
- }
- }
当浏览器 Cookie 没有禁用, 第一次访问该 servlet 时, 容器会采取双保险的策略, 既设置 cookie, 也在重写 url 时, 添加会话 ID 作为参数. 如下代码运行结果如下:
Cookie Allow, First Access in URL encode
可以看出, 容器对 URL 进行了重写, 在其中添加了 sessionid 参数.
但是, 当点击页面上的超链接, 再次访问该 servlet 时, 容器可以从重写的 url 中拿到会话 ID, 而且发现浏览器上送了 Cookie 信息. 这样, 容器就认为该浏览器没有禁用 cookie, 后续的访问中, 在对 url 进行重写时, 就不会添加会话 ID 参数了. 如下图:
Cookie Allow, Second Access in URL encode
如果这时候, 如果删除并禁用 Cookie, 然后, 通过页面上的链接 (链接中没有会话 ID 作为参数) 访问该 servlet, 这时候容器发现 Cookie 中没有上送会话 ID 信息, 而且 url 参数中也没有. 这时候, 容器又会在重写 url 时加入会话 ID 信息. 如下图:
Cookie Forbidden
此外, 如果想把请求重定向到另外一个 URL, 但是, 仍然想使用会话, 可以用一个专门的 url 重写方法:
reponse.encodeRedirectURL("/ServletUrlEncode.Test")
3, 总结
了解了 Java Web 开发中, 基本的会话管理机制, 对于后续使用会话有很大的帮助. 对于会话, 可以在其中设置属性, 添加监听等.
参考资料
Session (computer science)
JavaWeb 学习总结(十二)--Session https://www.cnblogs.com/xdp-gacl/p/3855702.html
来源: http://www.jianshu.com/p/90072d4fb1cf