【Java】会话

交换会话ID:使用cookie

HttpSession可以保存同一客户多个请求的会话状态。
HTTP协议使用的是无状态连接。浏览器与服务器建立连接,发送请求,得到响应,关闭连接。每次请求都是一个新的连接。因此,对容器而言,每次请求都源自新的客户。
那容器怎么知道客户是谁呢?
是这样的,对于客户的第一个请求,容器会生成唯一一个会话ID,并通过响应把它返回给客户。客户在以后的每个请求里都会发回这个会话ID,容器看到这个ID后,就会找到匹配的会话,并把这个会话和当前请求关联起来。
容器把会话ID作为响应的一部分返回给客户,客户把会话ID作为请求的一部分发回,那么,容器和客户是如何交换ID信息的呢?
——最常用最简单的方法,就是通过cookie。
在这里插入图片描述
HttpSession session = request.getSession(),向请求要一个会话,也就是告诉容器想使用一个会话,接下来容器会负责 生成会话ID、创建新的Cookie对象、将会话ID放入Cookie对象中、将Cookie对象设置为响应的一部分 等工作;
对于后续的请求,同样HttpSession session = request.getSession(),容器会从请求的Cookie对象中获取会话ID,然后找到匹配的会话,如果没有找到匹配的会话,则会新建一个会话,并将该会话与当前请求关联。至于会话到底是已经存在的,还是刚刚新建的,可以通过session.isNew()来判断。如果确定只想使用已经存在的会话,可以调用getSession(false),这个方法要么返回null,要么返回一个已经有的HttpSession

交换会话ID:使用URL重写

在服务器中调用request.getSession(),容器就会尝试使用cookie。但是,如果客户(浏览器)没有启用cookie,它就会忽略Set-Cookie响应首部,因此客户就不会加入会话,其后续的请求自然也不会发回带有会话ID的Cookie请求首部,同时意味着在服务中调用getSession()总会返回一个新的会话、session.isNew()总返回true
容器会首先使用cookie来进行会话管理,但是如果cookie方法失败,比如客户没有启用cookie,容器则会转而“投奔”URL重写。当然,URL重写奏效的前提是对URL完成了编码,且URL编码只与响应有关,response.encodeURL(url)response.encodeRedirectURL(url)

会话管理

删除不必要会话

会话会占用服务器资源,怎么管理会话呢?比如,删除不必要的会话。
HttpSession session = request.getSession(),首先我们来看下session这个HttpSession类实例有哪些方法。

方法名作用
getCreationTime()返回创建会话的时间
getLastAccessedTime()返回用户最后一次访问会话的时间
setMaxInactiveInterval()如果过去了指定的时间间隔,客户仍未对会话发出任何请求,会导致会话被撤销。这个方法可以用来减少服务器中的无用会话。
setMaxInactiveInterval(0)或setMaxInactiveInterval(-1)意味着会话永不超时
getMaxInactiveInterval()会话保持多久不活动仍能存活
invalidate()撤销会话
setAttribute(name,value)设置会话属性
getAttribute(name)获取会话属性
removeAttribute()删除会话属性

举个例子。

package com.example.web;


import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.example.model.*;


public class BeerSelect extends HttpServlet{
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException{
        PrintWriter out = response.getWriter();
        response.setContentType("text/html");
        HttpSession session = request.getSession();
        session.setAttribute("foo",10);
        session.invalidate();
        int foo = (int)session.getAttribute("foo");
        out.println("foo is "+foo);
    }
}

session.invalidate(),撤销了会话,因此后面调用session.getAttribute("foo")时会抛出运行时异常:java.lang.IllegalStateException
在这里插入图片描述

设置会话超时时间

虽然HttpSession有这么多方法,但并不意味着需要我们去跟踪会话行为、撤销会话,因为容器会负责这些事情,我们只需要设置会话超时时间。
设置会话超时时间有两种方式。

  • 第一种:在DD中设置会话超时时间,单位是“分钟”
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
<servlet>
    <servlet-name>Ch3 Beer</servlet-name>
    <servlet-class>com.example.web.BeerSelect</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Ch3 Beer</servlet-name>
    <url-pattern>/SelectBeer.do</url-pattern>
</servlet-mapping>
<session-config>
    <session-timeout>15</session-timeout>
</session-config>
</web-app>
  • 第二种:在程序中设置会话超时时间,单位是“秒”
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException{
	//...
    HttpSession session = request.getSession();
    session.setMaxInactiveInterval(15*60);
    //...
}

Cookie的其他用法

原先设计cookie是为了帮助支持会话状态,实际上,还可以使用cookie在客户和服务器之间交换一小段数据。服务器发送cookie给客户,客户在以后的每次请求里都返回cookie给服务器。

  • HttpServletRequest
    • getCookies()
      服务器从客户端请求中获取cookie
  • HttpServletResponse
    • addCookie()
      向响应增加一个Cookie
    • addHeader()
      向响应增加一个首部
    • setHeader()
      替换现有首部
  • Cookie
    • new Cookie(name,value)
      创建一个新的Cookie对象
    • setMaxAge()
      设置cookie能在客户端存活多久
    • getName()
    • getValue()

看一个例子:客户端从输入框中键入Username值,提交后发送请求到服务器,服务器中的servlet将该值保存在cookie中。当用户再次向服务器中任一servlet发送请求时,servlet都可以看到这个cookie。
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
<servlet>
    <servlet-name>CookieTester</servlet-name>
    <servlet-class>com.example.CookieTest</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>CookieTester</servlet-name>
    <url-pattern>/cookie-test.do</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>CookieChecker</servlet-name>
    <servlet-class>com.example.CookieCheck</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>CookieChecker</servlet-name>
    <url-pattern>/cookie-check.do</url-pattern>
</servlet-mapping>
</web-app>
<!-- form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>form</title>
</head>
<body>
    <form action="cookie-test.do" method="POST">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username">
        <input type="submit">
    </form>
</body>
</html>
<!-- cookieresult.jsp -->
<html>
    <body>
        <a href="cookie-check.do">click me</a>
    </body>
</html>
// CookieTest.java
package com.example;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CookieTest extends HttpServlet{
    
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException{
        response.setContentType("text/html");
        String username = request.getParameter("username");
        Cookie cookie = new Cookie("username",username);
        cookie.setMaxAge(30*60);
        response.addCookie(cookie);

        RequestDispatcher view = request.getRequestDispatcher("cookieresult.jsp");
        view.forward(request,response);

    }
}
// CookieCheck.java
package com.example;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CookieCheck extends HttpServlet{
    
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException{
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        Cookie[] cookies = request.getCookies();
        if(cookies!=null){
            for(Cookie cookie: cookies){
                if(cookie.getName().equals("username")){
                    String username = cookie.getValue();
                    out.println("username is "+ username);
                    break;
                }
            }
        }
    }
}

在这里插入图片描述

监听者们

HttpSessionListener

想知道一个Web应用中有多少个活动会话,可以使用HttpSessionListener

package com.example;

import javax.servlet.*;

public class SessionCounter implements HttpSessionListener{
    static private int activeSessions;
    public void sessionCreated(HttpSessionEvent e){
        activeSessions++;
    }
    public void sessionDestroyed(HttpSessionEvent e){
        activeSessions--;
    }
    public static void getActiveSessions(){
        return activeSessions;
    }
}

同时在部署描述文件中设置监听者。

<listener>
    <listener-class>com.example.SessionCounter</listener-class>
</listener>
HttpSessionAttributeListener

如果希望在会话中增加、删除或替换属性时得到通知,可以使用HttpSessionAttributeListener

package com.example;

import javax.servlet.*;

public class AttributeListener implements HttpSessionAttributeListener{
    public void attributeAdded(HttpSessionBindingEvent event){
        String name = event.getName();
        Object value = event.getValue();

    }
    public void attributeRemoved(HttpSessionBindingEvent event){
        String name = event.getName();
        Object value = event.getValue();

    }
    public void attributeReplaced(HttpSessionBindingEvent event){
        String name = event.getName();
        Object value = event.getValue();
    }
}

同时在部署描述文件中配置。

<listener>
    <listener-class>com.example.AttributeListener</listener-class>
</listener>
HttpSessionBindingListener

如果想在一个属性加入了会话时、属性从会话中删除时得到通知,可以使用HttpSessionBindingListener

package com.example;

import javax.servlet.*;

public class MyExample implements HttpSessionBindingListener{
    public void valueBound(HttpSessionBindingEvent event){

    }
    public void valueUnbound(HttpSessionBindingEvent event){
        
    }
}

HttpSessionListenerHttpSessionAttributeListenerHttpSessionActivationListener,需要在部署描述文件中设置,容器才能发现和使用这些监听者,而HttpSessionBindingListener不需要在部署描述文件中设置。

HttpSessionActivationListener

在一个分布式环境中,一个Web应用可能分布在多个JVM上,容器可能会为了完成负载均衡,将客户请求发送给多个JVM。这就意味着,指向ServletA的请求A在一个JVM中完成,而指向ServletA的请求B在另一个JVM中完成。
在一个分布式应用中,每个JVM上只有一个ServletContext,每个JVM中的每个Servlet只有一个ServletConfig,而对于一个应用的给定的会话ID,不论这个应用分布在多少个JVM中,都只有一个HttpSession,也就是说,只有HttpSession对象可以从一个JVM迁移到另一个JVM上。
HttpSession外,所有其他对象都会在另一个JVM(服务器)上复制,这就意味着,HttpSession对象可能从一个JVM迁移到另一个JVM。
序列化对象时会调用writeObject(),还原对象时会调用readObject(),但会话迁移不一定会调用这些方法。所以,在会话迁移过程中,要想保存和恢复 属性的实例变量的状态,可以使用HttpSessionActivationListener,调用其事件回调函数sessionDidActivate()sessionWillPassivate(),类似writeObject()readObject()

package com.example;

import javax.servlet.*;

public class Example implements HttpSessionActivationListener{
    public void sessionDidActivate(HttpSessionEvent event){

    }
    public void sessionWillPassivate(HttpSessionEvent event){

    }
}

同时在部署描述文件中进行配置。

<listener>
    <listener-class>com.example.Example</listener-class>
</listener>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值