了解 SessionDAO
类继承图

SessionDAO

AbstractSessionDAO

SessionIdGenerator sessionIdGenerator 用来生成 sessionId;
CachingSessionDAO
省略 getter 和 setter
EnterpriseCachingSessionDAO

MemorySessionDAO

理解 MemorySessionDAO
create(Session): Serializable
1. AbstractSessionDAO | create()
1 | // line 85 |
2 | /** |
3 | * Generates a new ID to be applied to the specified {@code session} instance. This method is usually called |
4 | * from within a subclass's {@link #doCreate} implementation where they assign the returned id to the session |
5 | * instance and then create a record with this ID in the EIS data store. |
6 | * <p/> |
7 | * Subclass implementations backed by EIS data stores that auto-generate IDs during record creation, such as |
8 | * relational databases, don't need to use this method or the {@link #getSessionIdGenerator() sessionIdGenerator} |
9 | * attribute - they can simply return the data store's generated ID from the {@link #doCreate} implementation |
10 | * if desired. |
11 | * <p/> |
12 | * This implementation uses the {@link #setSessionIdGenerator configured} {@link SessionIdGenerator} to create |
13 | * the ID. |
14 | * |
15 | * @param session the new session instance for which an ID will be generated and then assigned |
16 | * @return the generated ID to assign |
17 | */ |
18 | protected Serializable generateSessionId(Session session) { |
19 | if (this.sessionIdGenerator == null) { |
20 | String msg = "sessionIdGenerator attribute has not been configured."; |
21 | throw new IllegalStateException(msg); |
22 | } |
23 | return this.sessionIdGenerator.generateId(session); |
24 | } |
25 | |
26 | /** |
27 | * Creates the session by delegating EIS creation to subclasses via the {@link #doCreate} method, and then |
28 | * asserting that the returned sessionId is not null. |
29 | * |
30 | * @param session Session object to create in the EIS and associate with an ID. |
31 | */ |
32 | public Serializable create(Session session) { |
33 | // 调用 doCreate(),返回 sessionId |
34 | Serializable sessionId = doCreate(session); |
35 | verifySessionId(sessionId); |
36 | return sessionId; |
37 | } |
38 | /** |
39 | * Ensures the sessionId returned from the subclass implementation of {@link #doCreate} is not null and not |
40 | * already in use. |
41 | * |
42 | * @param sessionId session id returned from the subclass implementation of {@link #doCreate} |
43 | */ |
44 | private void verifySessionId(Serializable sessionId) { |
45 | if (sessionId == null) { |
46 | String msg = "sessionId returned from doCreate implementation is null. Please verify the implementation."; |
47 | throw new IllegalStateException(msg); |
48 | } |
49 | } |
50 | ...... |
51 | // line 156 |
52 | /** |
53 | * Subclass hook to actually persist the given <tt>Session</tt> instance to the underlying EIS. |
54 | * |
55 | * @param session the Session instance to persist to the EIS. |
56 | * @return the id of the session created in the EIS (i.e. this is almost always a primary key and should be the |
57 | * value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}. |
58 | */ |
59 | protected abstract Serializable doCreate(Session session); |
2. MemorySessionDAO | doCreate()
1 | // line 66 |
2 | protected Serializable doCreate(Session session) { |
3 | // 生成sessionId |
4 | Serializable sessionId = generateSessionId(session); |
5 | // 将sessionId填充到 session对象中 |
6 | assignSessionId(session, sessionId); |
7 | // 将session存储到ConcurrentHashMap对象中 |
8 | storeSession(sessionId, session); |
9 | return sessionId; |
10 | } |
11 | |
12 | protected Session storeSession(Serializable id, Session session) { |
13 | if (id == null) { |
14 | throw new NullPointerException("id argument cannot be null."); |
15 | } |
16 | return sessions.putIfAbsent(id, session); |
17 | } |
ConcurrentHashMap 是线程安全的 HashMap
小结
- 生成 sessionId (MemorySessionDAO)
- 将 sessionId 填充到 session中(MemorySessionDAO)
- 将 session 存储到 ConcurrentHashMap 对象sessions中,sessionId 作为key(MemorySessionDAO)
- 验证 sessionId 是否为空
- 返回 sessionId
readSession(Serializable): Session
1. AbstractSessionDAO | readSession()
1 | // line 158 |
2 | /** |
3 | * Retrieves the Session object from the underlying EIS identified by <tt>sessionId</tt> by delegating to |
4 | * the {@link #doReadSession(java.io.Serializable)} method. If {@code null} is returned from that method, an |
5 | * {@link UnknownSessionException} will be thrown. |
6 | * |
7 | * @param sessionId the id of the session to retrieve from the EIS. |
8 | * @return the session identified by <tt>sessionId</tt> in the EIS. |
9 | * @throws UnknownSessionException if the id specified does not correspond to any session in the EIS. |
10 | */ |
11 | public Session readSession(Serializable sessionId) throws UnknownSessionException { |
12 | Session s = doReadSession(sessionId); |
13 | if (s == null) { |
14 | throw new UnknownSessionException("There is no session with id [" + sessionId + "]"); |
15 | } |
16 | return s; |
17 | } |
18 | |
19 | /** |
20 | * Subclass implementation hook that retrieves the Session object from the underlying EIS or {@code null} if a |
21 | * session with that ID could not be found. |
22 | * |
23 | * @param sessionId the id of the <tt>Session</tt> to retrieve. |
24 | * @return the Session in the EIS identified by <tt>sessionId</tt> or {@code null} if a |
25 | * session with that ID could not be found. |
26 | */ |
27 | protected abstract Session doReadSession(Serializable sessionId); |
2. MemorySessionDAO | doReadSession()
1 | // line 80 |
2 | protected Session doReadSession(Serializable sessionId) { |
3 | return sessions.get(sessionId); |
4 | } |
update(Session): void
MemorySessionDAO | update()
1 | // line 72 |
2 | protected Session storeSession(Serializable id, Session session) { |
3 | if (id == null) { |
4 | throw new NullPointerException("id argument cannot be null."); |
5 | } |
6 | return sessions.putIfAbsent(id, session); |
7 | } |
8 | // line 84 |
9 | public void update(Session session) throws UnknownSessionException { |
10 | storeSession(session.getId(), session); |
11 | } |
delete(Session): void
MemorySessionDAO | delete()
1 | // line 88 |
2 | public void delete(Session session) { |
3 | if (session == null) { |
4 | throw new NullPointerException("session argument cannot be null."); |
5 | } |
6 | Serializable id = session.getId(); |
7 | if (id != null) { |
8 | sessions.remove(id); |
9 | } |
10 | } |
理解 EnterpiseCachingSessionDAO
create(Session): Serializable
1. CachingSessionDAO 中重写了 readSession()
1 | // line 177 |
2 | /** |
3 | * Calls {@code super.create(session)}, then caches the session keyed by the returned {@code sessionId}, and then |
4 | * returns this {@code sessionId}. |
5 | * |
6 | * @param session Session object to create in the EIS and then cache. |
7 | */ |
8 | public Serializable create(Session session) { |
9 | // 调用父类的create()方法,参考 MemorySessionDAO 中 AbstractSessionDAO 的 create() 方法 |
10 | // 内部调用 doCreate() ,然后验证 sessionId |
11 | Serializable sessionId = super.create(session); |
12 | // 缓存session |
13 | cache(session, sessionId); |
14 | return sessionId; |
15 | } |
2. EnterpriseCachingSessioDAO | doCreate()
1 | // line 63 |
2 | protected Serializable doCreate(Session session) { |
3 | Serializable sessionId = generateSessionId(session); |
4 | assignSessionId(session, sessionId); |
5 | return sessionId; |
6 | } |
3. CachingSessionDAO | cache(session, sessionId)
1 | ...... |
2 | // line 141 |
3 | /** |
4 | * Returns the active sessions cache, but if that cache instance is null, first lazily creates the cache instance |
5 | * via the {@link #createActiveSessionsCache()} method and then returns the instance. |
6 | * <p/> |
7 | * Note that this method will only return a non-null value code if the {@code CacheManager} has been set. If |
8 | * not set, there will be no cache. |
9 | * |
10 | * @return the active sessions cache instance. |
11 | */ |
12 | private Cache<Serializable, Session> getActiveSessionsCacheLazy() { |
13 | if (this.activeSessions == null) { |
14 | this.activeSessions = createActiveSessionsCache(); |
15 | } |
16 | return activeSessions; |
17 | } |
18 | |
19 | |
20 | // line 157 |
21 | /** |
22 | * Creates a cache instance used to store active sessions. Creation is done by first |
23 | * {@link #getCacheManager() acquiring} the {@code CacheManager}. If the cache manager is not null, the |
24 | * cache returned is that resulting from the following call: |
25 | * <pre> String name = {@link #getActiveSessionsCacheName() getActiveSessionsCacheName()}; |
26 | * cacheManager.getCache(name);</pre> |
27 | * |
28 | * @return a cache instance used to store active sessions, or {@code null} if the {@code CacheManager} has |
29 | * not been set. |
30 | */ |
31 | protected Cache<Serializable, Session> createActiveSessionsCache() { |
32 | Cache<Serializable, Session> cache = null; |
33 | CacheManager mgr = getCacheManager(); |
34 | if (mgr != null) { |
35 | String name = getActiveSessionsCacheName(); |
36 | cache = mgr.getCache(name); |
37 | } |
38 | return cache; |
39 | } |
40 | |
41 | ...... |
42 | |
43 | // line 220 |
44 | /** |
45 | * Caches the specified session under the cache entry key of {@code sessionId}. |
46 | * |
47 | * @param session the session to cache |
48 | * @param sessionId the session id, to be used as the cache entry key. |
49 | * @since 1.0 |
50 | */ |
51 | protected void cache(Session session, Serializable sessionId) { |
52 | if (session == null || sessionId == null) { |
53 | return; |
54 | } |
55 | // 获取 cache |
56 | Cache<Serializable, Session> cache = getActiveSessionsCacheLazy(); |
57 | if (cache == null) { |
58 | return; |
59 | } |
60 | // 将 session 存储到 cache 中,sessionId 作为 key |
61 | cache(session, sessionId, cache); |
62 | } |
63 | |
64 | /** |
65 | * Caches the specified session in the given cache under the key of {@code sessionId}. This implementation |
66 | * simply calls {@code cache.put(sessionId,session)} and can be overridden for custom behavior. |
67 | * |
68 | * @param session the session to cache |
69 | * @param sessionId the id of the session, expected to be the cache key. |
70 | * @param cache the cache to store the session |
71 | */ |
72 | protected void cache(Session session, Serializable sessionId, Cache<Serializable, Session> cache) { |
73 | cache.put(sessionId, session); |
74 | } |
EnterpriseCachingSessionDAO 构造函数中设置了默认的 CacheManager
1 | public EnterpriseCacheSessionDAO() { |
2 | setCacheManager(new AbstractCacheManager() { |
3 | |
4 | protected Cache<Serializable, Session> createCache(String name) throws CacheException { |
5 | return new MapCache<Serializable, Session>(name, new ConcurrentHashMap<Serializable, Session>()); |
6 | } |
7 | }); |
8 | } |
从中可以看出,实际中 MapCache 内部也是使用 ConcurrentHashMap对象来存储session的。
readSession(Serializable): Session
1. CachingSessionDAO | readSession()
1 | // line 250 |
2 | /** |
3 | * Attempts to acquire the Session from the cache first using the session ID as the cache key. If no session |
4 | * is found, {@code super.readSession(sessionId)} is called to perform the actual retrieval. |
5 | * |
6 | * @param sessionId the id of the session to retrieve from the EIS. |
7 | * @return the session identified by {@code sessionId} in the EIS. |
8 | * @throws UnknownSessionException if the id specified does not correspond to any session in the cache or EIS. |
9 | */ |
10 | public Session readSession(Serializable sessionId) throws UnknownSessionException { |
11 | // 根据 sessionId 获取缓存的 session |
12 | Session s = getCachedSession(sessionId); |
13 | if (s == null) { |
14 | // 调用父类方法 readSession 获取 session |
15 | // 其内部调用 doReadSession() 获取session |
16 | s = super.readSession(sessionId); |
17 | } |
18 | return s; |
19 | } |
20 | ...... |
21 | |
22 | // line 189 |
23 | /** |
24 | * Returns the cached session with the corresponding {@code sessionId} or {@code null} if there is |
25 | * no session cached under that id (or if there is no Cache). |
26 | * |
27 | * @param sessionId the id of the cached session to acquire. |
28 | * @return the cached session with the corresponding {@code sessionId}, or {@code null} if the session |
29 | * does not exist or is not cached. |
30 | */ |
31 | protected Session getCachedSession(Serializable sessionId) { |
32 | Session cached = null; |
33 | if (sessionId != null) { |
34 | Cache<Serializable, Session> cache = getActiveSessionsCacheLazy(); |
35 | if (cache != null) { |
36 | cached = getCachedSession(sessionId, cache); |
37 | } |
38 | } |
39 | return cached; |
40 | } |
41 | |
42 | /** |
43 | * Returns the Session with the specified id from the specified cache. This method simply calls |
44 | * {@code cache.get(sessionId)} and can be overridden by subclasses for custom acquisition behavior. |
45 | * |
46 | * @param sessionId the id of the session to acquire. |
47 | * @param cache the cache to acquire the session from |
48 | * @return the cached session, or {@code null} if the session wasn't in the cache. |
49 | */ |
50 | protected Session getCachedSession(Serializable sessionId, Cache<Serializable, Session> cache) { |
51 | return cache.get(sessionId); |
52 | } |
EnterpriseCachingSessionDAO 中实现了 doReadSession()
1 | protected Session doReadSession(Serializable sessionId) { |
2 | return null; //should never execute because this implementation relies on parent class to access cache, which |
3 | //is where all sessions reside - it is the cache implementation that determines if the |
4 | //cache is memory only or disk-persistent, etc. |
5 | } |
update(Session): void
1. CachingSessionDAO 中实现了 update()
1 | // line 266 |
2 | /** |
3 | * Updates the state of the given session to the EIS by first delegating to |
4 | * {@link #doUpdate(org.apache.shiro.session.Session)}. If the session is a {@link ValidatingSession}, it will |
5 | * be added to the cache only if it is {@link ValidatingSession#isValid()} and if invalid, will be removed from the |
6 | * cache. If it is not a {@code ValidatingSession} instance, it will be added to the cache in any event. |
7 | * |
8 | * @param session the session object to update in the EIS. |
9 | * @throws UnknownSessionException if no existing EIS session record exists with the |
10 | * identifier of {@link Session#getId() session.getId()} |
11 | */ |
12 | public void update(Session session) throws UnknownSessionException { |
13 | doUpdate(session); |
14 | // 判断session是否是 ValidatingSession 实例 |
15 | if (session instanceof ValidatingSession) { |
16 | // 判断 session 是否有效 |
17 | if (((ValidatingSession) session).isValid()) { |
18 | cache(session, session.getId()); |
19 | } else { |
20 | uncache(session); |
21 | } |
22 | } else { |
23 | cache(session, session.getId()); |
24 | } |
25 | } |
2. EnterpriseCachingSessionDAO | doUpdate()
1 | protected void doUpdate(Session session) { |
2 | //does nothing - parent class persists to cache. |
3 | } |
delete(Session): void
1. CachingSessionDAO | delete()
1 | /** |
2 | * Removes the specified session from any cache and then permanently deletes the session from the EIS by |
3 | * delegating to {@link #doDelete}. |
4 | * |
5 | * @param session the session to remove from caches and permanently delete from the EIS. |
6 | */ |
7 | public void delete(Session session) { |
8 | // 删除缓存中的session |
9 | uncache(session); |
10 | // 调用 doDelete() |
11 | doDelete(session); |
12 | } |
2. EnterpriseCachingSessionDAO | doDelete()
1 | protected void doDelete(Session session) { |
2 | //does nothing - parent class removes from cache. |
3 | } |
总结
SessionDAO用于 session 存储,它有两个实现:
MemorySessionDAO使用ConcurrentHashMap对象将 session 保存到内存中EnterpriseCachingSessionDAO将session保存到缓存中(该缓存默认使用ConcurrentHashMap保存到内存中)。其实EnterpriseCachingSessionDAO就是一个对外的接口,用户自定义SessionDAO时,只需继承他。