`
haitaoandroid
  • 浏览: 26518 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Tomcat源码分析(九)--Session管理

 
阅读更多
在明白Tomcat的Session机制之前,先要了解Session,Cookie,JSESSIONID这几个概念。JSESSIONID是一个唯一标识号,用来标识服务器端的Session,也用来标识客户端的Cookie,客户端和服务器端通过这个JSESSIONID来一一对应。这里需要说明的是Cookie已经包含JSESSIONID了,可以理解为JSESSIONID是Cookie里的一个属性。让我假设一次客户端连接来说明我对个这三个概念的理解:
Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,但是互联网访问基本上都是需要有状态的,即服务器需要知道两次连接请求是不是同一个人访问的。如你在浏览淘宝的时候,把一个东西加入购物车,再点开另一个商品页面的时候希望在这个页面里面的购物车还有上次添加进购物车的商品。也就是说淘宝服务器会知道这两次访问是同一个客户端访问的。
客户端第一次请求到服务器连接,这个连接是没有附带任何东西的,没有Cookie,没有JSESSIONID。服务器端接收到请求后,会检查这次请求有没有传过来JSESSIONID或者Cookie,如果没有JSESSIONID和Cookie,则服务器端会创建一个Session,并生成一个与该Session相关联的JSESSIONID返回给客户端,客户端会保存这个JSESSIONID,并生成一个与该JSESSIONID关联的Cookie,第二次请求的时候,会把该Cookie(包含JSESSIONID)一起发送给服务器端,这次服务器发现这个请求有了Cookie,便从中取出JSESSIONID,然后根据这个JSESSIONID找到对应的Session,这样便把Http的无状态连接变成了有状态的连接。但是有时候浏览器(即客户端)会禁用Cookie,我们知道Cookie是通过Http的请求头部的一个cookie字段传过去的,如果禁用,那么便得不到这个值,JSESSIONID便不能通过Cookie传入服务器端,当然我们还有其他的解决办法,url重写和隐藏表单,url重写就是把JSESSIONID附带在url后面传过去。隐藏表单是在表单提交的时候传入一个隐藏字段JSESSIONID。这两种方式都能把JSESSIONID传过去。
下面来看Tomcat是怎么实现以上流程的。从Tomcat源码分析(二)--连接处理可知,连接请求会交给HttpProcessor的process方法处理,在此方法有这么几句:
                    parseConnection(socket);
                    parseRequest(input, output);//解析请求行,如果有jessionid,会在方法里面解析jessionid
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);//解析请求头部,如果有cookie字段,在方法里面会解析cookie,
下面看parseRequest方法里面是怎么解析jessionid的,这种解析方式是针对url重写的:

parseRequest方法:
int semicolon = uri.indexOf(match);//match是“;JSESSIONID=”,即在请求行查找字段JSESSIONID
        if (semicolon >= 0) {                                   //如果有JSESSIONID字段,表示不是第一次访问
            String rest = uri.substring(semicolon + match.length());
            int semicolon2 = rest.indexOf(';');
            if (semicolon2 >= 0) {
                request.setRequestedSessionId(rest.substring(0, semicolon2));//设置sessionid
                rest = rest.substring(semicolon2);
            } else {
                request.setRequestedSessionId(rest);
                rest = "";
            }
            request.setRequestedSessionURL(true);
            uri = uri.substring(0, semicolon) + rest;
            if (debug >= 1)
                log(" Requested URL session id is " +
                    ((HttpServletRequest) request.getRequest())
                    .getRequestedSessionId());
        } else {                               //如果请求行没有JSESSIONID字段,表示是第一次访问。
            request.setRequestedSessionId(null);
            request.setRequestedSessionURL(false);
        }

代码没什么说的,看url有没有JSESSIONID,有就设置request的sessionid,没有就设置为null。有再看parseHeaders方法:
parseHeaders方法:
.....
....else if (header.equals(DefaultHeaders.COOKIE_NAME)) { //COOKIE_NAME的值是cookie
                Cookie cookies[] = RequestUtil.parseCookieHeader(value);
                for (int i = 0; i < cookies.length; i++) {
                    if (cookies[i].getName().equals
                        (Globals.SESSION_COOKIE_NAME)) {
                        // Override anything requested in the URL
                        if (!request.isRequestedSessionIdFromCookie()) {
                            // Accept only the first session id cookie
                            request.setRequestedSessionId
                                (cookies[i].getValue());//设置sessionid
                            request.setRequestedSessionCookie(true);
                            request.setRequestedSessionURL(false);
                            if (debug >= 1)
                                log(" Requested cookie session id is " +
                                    ((HttpServletRequest) request.getRequest())
                                    .getRequestedSessionId());
                        }
                    }
                    if (debug >= 1)
                        log(" Adding cookie " + cookies[i].getName() + "=" +
                            cookies[i].getValue());
                    request.addCookie(cookies[i]);
                }
            } 
代码主要就是从http请求头部的字段cookie得到JSESSIONID并设置到reqeust的sessionid,没有就不设置。这样客户端的JSESSIONID(cookie)就传到tomcat,tomcat把JSESSIONID的值赋给request了。这个request在Tomcat的唯一性就标识了。
我们知道,Session只对应用有用,两个应用的Session一般不能共用,在Tomcat一个Context代表一个应用,所以一个应用应该有一套自己的Session,Tomcat使用Manager来管理各个应用的Session,Manager也是一个组件,跟Context是一一对应的关系,怎么关联的请参考Tomcat源码分析(一)--服务启动,方法类似。Manager的标准实现是StandardManager,由它统一管理Context的Session对象(标准实现是StandardSession),能够猜想,StandardManager一定能够创建Session对象和根据JSESSIONID从跟它关联的应用中查找Session对象。事实上StandardManager确实有这样的方法,但是StandardManager本身没有这两个方法,它的父类ManagerBase有这两个方法
ManagerBase类的findSession和createSession()方法
public Session findSession(String id) throws IOException {
        if (id == null)
            return (null);
        synchronized (sessions) {
            Session session = (Session) sessions.get(id);//根据sessionid(即JSESSIONID)查找session对象。
            return (session);
        }
    }
public Session createSession() { //创建session对象
        // Recycle or create a Session instance
        Session session = null;
        synchronized (recycled) {
            int size = recycled.size();
            if (size > 0) {
                session = (Session) recycled.get(size - 1);
                recycled.remove(size - 1);
            }
        }
        if (session != null)
            session.setManager(this);
        else
            session = new StandardSession(this);

        // Initialize the properties of the new session and return it
        session.setNew(true);
        session.setValid(true);
        session.setCreationTime(System.currentTimeMillis());
        session.setMaxInactiveInterval(this.maxInactiveInterval);
        String sessionId = generateSessionId();//使用md5算法生成sessionId
        String jvmRoute = getJvmRoute();
        // @todo Move appending of jvmRoute generateSessionId()???
        if (jvmRoute != null) {
            sessionId += '.' + jvmRoute;
            session.setId(sessionId);
        }
        session.setId(sessionId);
        return (session);
    }
以上是StandardManager的管理Session的两个重要方法。这里有一个问题,Session是在什么时候生成的?仔细想想,我们编写servlet的时候,如果需要用到Session,会使用request.getSession(),这个方法最后会调用到HttpRequestBase的getSession()方法,所以这里有个重要的点:Session并不是在客户端第一次访问就会在服务器端生成,而是在服务器端(一般是servlet里)使用request调用getSession方法才生成的。但是默认情况下,jsp页面会调用request.getSession(),即jsp页面的这个属性<%@ page session="true" %>默认是true的,编译成servlet后会调用request.getSession()。所以只要访问jsp页面,一般是会在服务器端创建session的。但是在servlet里就需要显示的调用getSession(),当然是在要用session的情况。下面看这个getSession()方法:
HttpRequestBase.getSession()
   调用---------------》
    HttpRequestBase.getSession(boolean create)
       调用 ----------------》
            HttpRequestBase.doGetSession(boolean create){
      if (context == null)
            return (null);
   
        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid())
            session = null;
        if (session != null)
            return (session.getSession());
        // Return the requested session if it exists and is valid
        Manager manager = null;
        if (context != null)
            manager = context.getManager();

        if (manager == null)
            return (null);      // Sessions are not supported

        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);//这里调用StandardManager的findSession方法查找是否存在Session对象
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid())
                session = null;
            if (session != null) {
                return (session.getSession());
            }
        }

        // Create a new session if requested and the response is not committed
        if (!create)
            return (null);
        if ((context != null) && (response != null) &&
            context.getCookies() &&
            response.getResponse().isCommitted()) {
            throw new IllegalStateException
              (sm.getString("httpRequestBase.createCommitted"));
        }

        session = manager.createSession();//这里调用StandardManager的创建Session对象
        if (session != null)
            return (session.getSession());
        else
            return (null);
}     

至此,Tomcat的Session管理的大部分东西也写的差不多了,这里没有写StandardManager和StandardSession两个类以及他们的实现接口,还有继承关系等,是因为觉得这篇文章已经够长了,而且他们跟跟其他标准组件也差不多,无非是实现的具体功能不一样,比如StandardSession还有过期处理等,不过它们也跟其他组件有各种关系,比如StandardManager就跟Context容器是关联的。有机会再细细的说Session管理器其他的东西(持久化和分布式)。
分享到:
评论

相关推荐

    深入浅析TomCat Session管理分析

    主要介绍了深入浅析TomCat Session管理分析,需要的朋友可以参考下

    单点登录源码

    - 统一下单(统一下单接口、统一扫码)、订单管理、数据分析、财务报表、商户管理、渠道管理、对账系统、系统监控。 ![统一扫码支付](project-bootstrap/zheng-pay.png) &gt; zheng-ucenter 通用用户管理系统, 实现...

    spring mvc项目

    可用于分析spring mvc源码、spring mvc父子容器初始化流程、session和cookie机制、spring session等,也可以用于学习Java Web(servlet、filter、listener等)、spring源码等。 该项目使用servlet3.0规范,无web.xml...

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来...

    黑马程序员安卓Android52期培训课

    2015/4/11 星期六 android案例与项目-android源码级分析 2015/4/12 星期日 项目管理与项目实施 2015/4/13 星期一 2015/4/14 星期二 android项目实战-手机影音 2015/4/15 星期三 android项目实战-手机影音 2015/4/16...

    基于JavaWeb的在线问卷调查系统源码+项目部署说明.zip

    基于JavaWeb的在线问卷调查系统源码+项目部署说明.zip 该项目是个人毕设项目,答辩评审分达到95分,代码都经过调试测试,确保可以运行!欢迎下载使用,可用于小白学习、进阶。 该资源主要针对计算机、通信、人工智能...

    JAVA上百实例源码以及开源项目

    笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此...

    基于BootStrap+JSP+Servlet+Mysql的模拟咖啡厅点餐系统源码+数据库+项目说明.zip

    环境配置(Tomcat与JDK的版本使用问题,DBCP/C3P0数据库连接池的配置问题) 2. 登录(会话)维持————session/cookies 3. 分页显示————PageModel/jdbc-mysql查 ### 实现图 ![主页]...

    基于javatcpsocket通信的拆包和装包源码-distributed:分散式

    存sessionid,服务端Tomcat用ConcuurentMap存储 session sticky (请求落在同一个服务器上) session replication (复制) session 集中存储 db、缓存服务器 cookie (主流)保存在客户端 access token(userid/tok

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    主要从对项目的经济上进行分析评价,一方面是支出的费用,包括设备购置费、管理和维护费用、人员工资和培训费等,另一个是取得的收益。这是个小型的系统,从投入的人力,财力与物力来讲是非常小的,发布出去之需要...

    《程序天下:J2EE整合详解与典型案例》光盘源码

    12.2.3 Session(提供Connection) 12.3 使用Hibernate操作数据库的示例 12.3.1 创建数据库表 12.3.2 编写表对应的JavaBean 12.3.3 编写JavaBean对应的Xml 12.3.4 编写Hibernate的配置文件 12.3.5 编写并验证测试...

    java开源包8

    jSIP这个Java包目标是用Java实现SIP(SIP:Session Initiation Protocol)协议及SIP协议的其它扩展部 分。 Java表达式语法解析库 parboiled parboiled 是一个纯Java库提供了一种轻量级,易于使用,功能强大和优雅的PEG...

    JSP网站开发典型模块与实例精讲

     3.1 功能分析及数据库设计  3.1.1 功能概述  3.1.2 数据库表结构设计  3.1.3 独家见解——在设计表的时候少  3.1.3 用外关键字  3.2 讨论区管理设计  3.2.1 指点迷津——使用DataSource接口  3.2.1...

    java开源包1

    jSIP这个Java包目标是用Java实现SIP(SIP:Session Initiation Protocol)协议及SIP协议的其它扩展部 分。 Java表达式语法解析库 parboiled parboiled 是一个纯Java库提供了一种轻量级,易于使用,功能强大和优雅的PEG...

    java开源包10

    jSIP这个Java包目标是用Java实现SIP(SIP:Session Initiation Protocol)协议及SIP协议的其它扩展部 分。 Java表达式语法解析库 parboiled parboiled 是一个纯Java库提供了一种轻量级,易于使用,功能强大和优雅的PEG...

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    是PDF电子书,不是源码。共分2个包。 《Java Web编程宝典(十年典藏版)》是一本集技能、范例、项目和应用为一体的学习手册,书中介绍了应用Java Web进行程序开发的各种技术、技巧。全书分4篇,共24章,其中,第1篇为...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (2)

    一共四个,其中pdf 三个包,源码一个包 第一章 J2EE快速入门 1.1 J2EE概述 1.1.1 J2EE的来源 1.1.2 J2EE整体框架 1.1.3 从J2EE到JavaEE 1.2 J2EE组件 1.2.1 客户端组件 1.2.2 Web组件 1.2.3 业务逻辑组件 1.3 J2EE...

Global site tag (gtag.js) - Google Analytics