還記得當初寫spring-session實現分佈式集羣session的共享的時候,裏面有說到利用filter和HttpServletRequestWrapper能夠定製本身的getSession方法,實現對session的控制,從而將session存放到統一的位置進行存儲,達到session共享的目的。可是具體是如何實現的沒有說起,今天咱們就本身實現一個簡單的session共享。html
路漫漫其修遠兮,吾將上下而求索!java
github:https://github.com/youzhibinggit
碼雲(gitee):https://gitee.com/youzhibinggithub
進入正題以前咱們先來看看另一個內容,放鬆下心情。儘管目前房價依舊很高,但仍是阻止不了你們對新房的渴望和買房的熱情。若是你們買的是毛坯房,無疑還有一項艱鉅的任務要面對,那就是裝修。對新房進行裝修並無改變房屋用於居住的本質,但它可讓房子變得更漂亮、更舒適、更實用、更能知足居家的需求。在軟件設計中,咱們也有一種相似新房裝修的技術能夠對已有對象(新房)的功能進行擴展(裝修),以得到更加符合用戶需求的對象,使得對象具備更增強大的功能。這種技術對應於一種被稱之爲裝飾模式的設計模式。spring
裝飾者模式又名包裝模式,以對客戶端透明的方式拓展對象的功能,可以讓咱們在不修改底層代碼的狀況下,給咱們的對象賦予新的職責。是繼承關係的一個替代方案。設計模式
裝飾模式中的角色: 瀏覽器
抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
具體構件(ConcreteComponent)角色:定義一個將要接收附加責任的類。
裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。
具體裝飾(ConcreteDecorator)角色:負責給構件對象「貼上」附加的責任緩存
Component.javatomcat
public interface Component { void sampleOperation(); }
ConcreteComponent.java服務器
public class ConcreteComponent implements Component { @Override public void sampleOperation() { // 寫具體業務代碼 System.out.println("我是ConcreteComponent"); } }
Decorator.java
public class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } @Override public void sampleOperation() { // 委派給具體的構建 component.sampleOperation(); } }
ConcreteDecorator.java
public class ConcreteDecorator extends Decorator{ public ConcreteDecorator(Component component) { super(component); } @Override public void sampleOperation() { // 寫相關的業務代碼 System.out.println("調用component方法以前業務處理"); super.sampleOperation(); // 寫相關的業務代碼 System.out.println("調用component方法以後業務處理"); } }
更多詳情在spring-boot-test下的com.lee.decorator包下
裝飾模式在Java語言中的最著名的應用莫過於Java I/O標準庫的設計了。因爲Java I/O庫須要不少性能的各類組合,若是這些性能都是用繼承的方法實現的,那麼每一種組合都須要一個類,這樣就會形成大量性能重複的類出現。而若是採用裝飾模式,那麼類的數目就會大大減小,性能的重複也能夠減至最少,所以裝飾模式是Java I/O庫的基本模式。
因爲Java I/O的對象衆多,這裏只畫出InputStream的一部分
咱們來捋一捋這個類圖在裝飾模式中角色的對應
抽象構件(Component)角色:InputStream,這是一個抽象類,爲各類子類型提供統一的接口
具體構件(ConcreteComponent)角色:FileInputStream,實現了抽象構件角色所規定的接口
裝飾(Decorator)角色:FilterInputStream,它實現了InputStream所規定的接口
具體裝飾(ConcreteDecorator)角色:BufferedInputStream
咱們先來看看一個請求的發起到響應的時序圖
Interceptor依賴具體的框架(固然咱們也能夠本身實現),不是Servlet的內容,暫且先將其拋開,那麼至關於請求先通過Filter鏈,再到Servlet,而後servlet處理完以後,再通過Filter鏈返回給瀏覽器。
此時咱們要對session的獲取進行定製,咱們能怎麼處理?兩種選擇,一是從Servlet入手,二是從Filter入手。那咱們想想,從Servlet入手可行嗎?可行,只是可行性很是低,由於咱們須要定製的東西就太多了,容器的那套Servlet規範實現咱們都須要本身來實現了。若是從Filter入手,咱們能夠繼續沿用容器的那套實現,並從中插入咱們的定製內容,那麼改動的內容就不多了。具體如何實現,咱們一塊兒往下看
在實現咱們本身的session管理以前,咱們先來看看session在servlet容器中的建立。
客戶端第一次請求request.getSession()時,也就是說客戶端的請求中服務端第一次調用request.getSession()時,服務器會建立了Session對象並保存在servlet容器的session集合中,同時生成一個Session id,並經過響應頭的Set-Cookie命令,向客戶端發送要求設置cookie的響應(cookie中設置Session id信息),客戶端收到響應後,在客戶端設置了一個JSESSIONID=XXXXXXX的cookie信息;接下來客戶端每次向服務器發送請求時,請求頭都會帶上該cookie信息(包含Session id),那麼以後的每次請求都能從servlet容器的session集合中找到客戶端對應的session了,這樣也就至關於保持了用戶與服務器的交互狀態。
注意:
第一次請求request.getSession()時,請求頭沒帶session id的信息,響應頭中包括設置session id的cookie設置命令;以後客戶端的請求(無論服務端時候調用request.getSession()),請求頭都有session id信息,而響應頭不再會有設置session id的cookie設置命令
session以及session id是在第一次調用request.getSession()時建立的(session過時另說,不是本文內容)
不一樣容器的session id名稱可能不同,JSESSIONID是tomcat中session id的默認名
不依賴任何框架,就用Filter + HttpServletRequestWrapper實現咱們本身的簡單session管理。自定義Filter的做用是在請求到達Servlet以前,咱們將HttpServletRequest封裝成咱們本身的HttpServletRequestWrapper實現類:CustomizeSessionHttpServletRequest,那麼到達Servlet的HttpServletRequest對象其實是CustomizeSessionHttpServletRequest;咱們重寫CustomizeSessionHttpServletRequest的getSession方法,使其從咱們本身的session容器中獲取,從而實現session的自定義管理。爲了實現同一會話的效果,在建立session的時候,須要往response中添加cookie,保存session id,下次請求的時候,瀏覽器會將cookie信息傳過來,咱們去cookie中獲取session id,根據session id取session容器獲取session,這樣就能保證同一會話效果了。
具體代碼這裏就不貼了,你們去查看customize-session,效果以下
先訪問http://localhost:8083/customize-session/test,此時是沒有產生session的,http://localhost:8083/customize-session/請求的是index.jsp,jsp請求了內置對象session,此時產生session,並讓瀏覽器設置緩存,那麼以後的每次請求都會帶上包含session id的緩存。
ServletRequestWrapper中有成員變量ServletRequest request;
不是嚴格意義上的裝飾模式
抽象構件(Component)角色:ServletRequest
具體構件(ConcreteComponent)角色:無
裝飾(Decorator)角色:ServletRequestWrapper
具體裝飾(ConcreteDecorator)角色:CustomizeHttpServletRequest
一、裝飾模式
文中裝飾模式講的不是很細,你們若是有什麼不懂的地方能夠去我參考的兩本的兩本書中尋找更詳細的信息。
jdk源碼中,I/O標準庫大量用到了裝飾模式和適配器模式,有興趣的小夥伴能夠去詳細的看看。
二、自定義session管理
Filter攔截請求,將HttpServletRequest封裝成咱們本身的CustomizeSessionHttpServletRequest,進而插入咱們的session建立與獲取邏輯,由於session的獲取方式每每是:request.getSession();
往response中添加cookie,須要在response提交以前,不然添加無效;
另外咱們自定義了HttpSession:CustomizeSession,目的是爲了更好地控制session
三、不足
首先強調一點:方向與思路是沒錯的!
目前只是實現了session的建立與獲取,實現的還比較通常,提高空間比較大;session管理還包括:session過時、session刷新等;另外session的存儲在本文中寫死了,沒有對外提交接口實現多方式存儲,好的方式應該是對外提供接口並提供默認實現。
四、目的
寫本文的目的只是讓你們對自定義session的管理有個簡單的認知,若是直接從shiro的session管理,或者spring-session的session管理入口,咱們可能不知道如何去閱讀,畢竟這二者是個成熟的體系,涉及的內容不少,咱們可能會望而卻步了;但無論怎樣,實現方式都是同樣的,只是shiro、spring-session在此基礎上進行各類內容豐富,使得體系愈發成熟。
爲個人shiro源碼篇 - shiro的session共享,你值得擁有作準備
《Head First 設計模式》
《Java與模式》