Tomcat源码分析(九)--Session管理
生活随笔
收集整理的這篇文章主要介紹了
Tomcat源码分析(九)--Session管理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本系列轉載自?http://blog.csdn.net/haitao111313/article/category/1179996??parseConnection(socket);?? parseRequest(input,?output);//解析請求行,如果有jessionid,會在方法里面解析jessionid?? if?(!request.getRequest().getProtocol()?? ????.startsWith("HTTP/0"))?? ????parseHeaders(input);//解析請求頭部,如果有cookie字段,在方法里面會解析cookie,?? 下面看parseRequest方法里面是怎么解析jessionid的,這種解析方式是針對url重寫的:[java]?view plaincopyprint?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方法:[java]?view plaincopyprint?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有這兩個方法:[java]?view plaincopyprint?ManagerBase類的findSession和createSession()方法?? public?Session?findSession(String?id)?throws?IOException?{?? ????????if?(id?==?null)?? ????????????return?(null);?? ????????synchronized?(sessions)?{?? ????????????Session?session?=?(Session)?sessions.get(id);//根據sessionid(即<span?style="font-family:?Arial;?">JSESSIONID</span>)查找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()方法:[java]?view plaincopyprint?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機制之前,先要了解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方法處理,在此方法有這么幾句:[java]?view plaincopyprint?代碼沒什么說的,看url有沒有JSESSIONID,有就設置request的sessionid,沒有就設置為null。有再看parseHeaders方法:[java]?view plaincopyprint?
? ? ? ? 至此,Tomcat的Session管理的大部分東西也寫的差不多了,這里沒有寫StandardManager和StandardSession兩個類以及他們的實現接口,還有繼承關系等,是因為覺得這篇文章已經夠長了,而且他們跟跟其他標準組件也差不多,無非是實現的具體功能不一樣,比如StandardSession還有過期處理等,不過它們也跟其他組件有各種關系,比如StandardManager就跟Context容器是關聯的。有機會再細細的說Session管理器其他的東西(持久化和分布式)。
總結
以上是生活随笔為你收集整理的Tomcat源码分析(九)--Session管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse中显示jquery或ext
- 下一篇: 生产环境该如何选择lvs的工作模式,和哪