SessionDAO
是用于session持久化的,SessionManager
只是负责session的管理,持久化的工作是由SessionDAO
完成的。
SessionDAO 接口
SessionDAO
接口是Shiro中所有SessionDAO的顶级接口,给出了从持久层(一般而言是关系型数据库)操作session的标准。方法如下:
-
Serializable create(Session session);
插入一个新的Session
到持久化设备。 -
Session readSession(Serializable sessionId) throws UnknownSessionException;
根据会话ID
获取会话。 -
void update(Session session) throws UnknownSessionException;
更新session; 如更新session最后访问时间/停止会话/设置超时时间/设置移除属性等会调用。 -
void delete(Session session);
删除session; 当会话过期/会话停止(如用户退出时)会调用。
AbstractSessionDAO 抽象类
AbstractSessionDAO
提供了SessionDAO
的基本实现;
/**
* SessionDAO中create实现; 对创建的sesion进行持久化
* 子类doCreate方法的代理,具体的细节委托给了子类的doCreate方法
*/
public Serializable create(Session session) {
Serializable sessionId = doCreate(session);
verifySessionId(sessionId);
return sessionId;
}
/**
* 子类通过实现此方法来持久化Session
*/
protected abstract Serializable doCreate(Session session);
/**
* 保证从doCreate返回的sessionId不是null,并且不是已经存在的.
* 目前只实现了null校验,是否已存在没有校验
*/
private void verifySessionId(Serializable sessionId) {
if (sessionId == null) {
String msg = "sessionId returned from doCreate implementation is null. Please verify the implementation.";
throw new IllegalStateException(msg);
}
}
如上代码所示,AbstractSessionDAO
实现了SessionDao
的create
方法;并把具体的创建并持久化Session工作(doCreate
方法)留给子类来实现。
/**
* SessionDAO中readSession实现; 通过sessionId从持久化设备获取session对象.
* 子类doReadSession方法的代理,具体的获取细节委托给了子类的doReadSession方法.
*/
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = doReadSession(sessionId);
if (s == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
}
return s;
}
/**
* 子类通过实现此方法从持久化设备获取session实例
*/
protected abstract Session doReadSession(Serializable sessionId);
如上代码所示,AbstractSessionDAO
实现了SessionDao
的readSession
方法;并把具体的从持久化设备获取session的操作留给了子类来实现。
AbstractSessionDAO
还实现了自己的sessionId
生成器,负责sessionId
的操作。代码如下:
/**
* sessionId生成器
*/
private SessionIdGenerator sessionIdGenerator;
public AbstractSessionDAO() {
// 指定JavaUuidSessionIdGenerator为默认sessionId生成器
this.sessionIdGenerator = new JavaUuidSessionIdGenerator();
}
/**
* 获取sessionId生成器
*/
public SessionIdGenerator getSessionIdGenerator() {
return sessionIdGenerator;
}
/**
* 设置sessionId生成器
*/
public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {
this.sessionIdGenerator = sessionIdGenerator;
}
/**
* 生成一个新的sessionId, 并将它应用到session实例
*/
protected Serializable generateSessionId(Session session) {
if (this.sessionIdGenerator == null) {
String msg = "sessionIdGenerator attribute has not been configured.";
throw new IllegalStateException(msg);
}
return this.sessionIdGenerator.generateId(session);
}
CachingSessionDAO 抽象类
CachingSessionDAO
承继自AbstractSessionDAO
,并实现了CacheManagerAware
接口提供了session缓存的功能。它是应用层与持久化层之间的缓存层,不用频繁请求持久化层以提升效率。
注:需要特别说明的是,CachingSessionDAO
仅是提供了缓存功能,但并没有实现缓存功能,具体的缓存功能和缓存方案还需要由子类来完成。
- 重写
AbstractSessionDAO
中的create
如下:
/**
* AbstractSessionDAO中create的重写
* 调用父类(AbstractSessionDAO)的create方法, 然后将session缓存起来
* 返回sessionId
*/
public Serializable create(Session session) {
Serializable sessionId = super.create(session); // 调用父类的create方法
cache(session, sessionId); // 以sessionId作为key缓存session
return sessionId;
}
- 重写
AbstractSessionDAO
中的readSession
如下:
/**
* AbstractSessionDAO中readSession的重写
* 先从缓存中获取,若没有则调用父类的readSession方法获取session
*/
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = getCachedSession(sessionId); // 从缓存中获取
if (s == null) {
s = super.readSession(sessionId); // 调用父类readSession方法获取
}
return s;
}
- 实现了
SessionDAO
中的update
方法如下:
/**
* SessionDAO中update的实现
* 更新session的状态
*/
public void update(Session session) throws UnknownSessionException {
doUpdate(session); // 更新持久化设备中的session
if (session instanceof ValidatingSession) {
if (((ValidatingSession) session).isValid()) {
cache(session, session.getId()); // 更新缓存中的session
} else {
uncache(session); // 移除缓存中的sesson
}
} else {
cache(session, session.getId());
}
}
/**
* 由子类去实现,持久化session到EIS
*/
protected abstract void doUpdate(Session session);
在update
方法中,CachingSessionDAO
仅实现了缓存功能,而具体的持久化操作则留出了doUpdate
抽象方法方法由子类来实现。
- 实现了
SessionDAO
中的delete
方法如下:
/**
* SessionDAO中delete的实现
* 删除session
*/
public void delete(Session session) {
uncache(session); // 从缓存中移除
doDelete(session); // 从EIS中删除
}
/**
* 由子类去实现,从EIS中删除session
*/
protected abstract void doDelete(Session session);
同样的,在delete
方法中,CachingSessionDAO
也仅实现了缓存功能,而具体的持久化操作则留出了doDelete
抽象方法方法由子类来实现。
- 实现了
SessionDAO
中的getActiveSessions
方法如下:
/**
* SessionDAO中getActiveSessions的实现
* 获取所有的存活的session
*/
public Collection getActiveSessions() {
Cache cache = getActiveSessionsCacheLazy();
if (cache != null) {
return cache.values();
} else {
return Collections.emptySet();
}
}
MemorySessionDAO 实现类
MemorySessionDAO
是SessionDAO
的简单内存实现。将session保存在内存中,存储结构是ConcurrentHashMap;项目中基本上用不到,就不详细说明了。
EnterpriseCacheSessionDAO 实现类
EnterpriseCacheSessionDAO
继承自CachingSessionDAO
,在构造器中设置了默认的缓存管理器(AbstractCacheManager)和默认的缓存实例(MapCache),实现了缓存效果。代码如下:
public EnterpriseCacheSessionDAO() {
// 设置默认缓存器,并实例化MapCache作为cache实例
setCacheManager(new AbstractCacheManager() {
@Override
protected Cache createCache(String name) throws CacheException {
return new MapCache(name,
new ConcurrentHashMap());
}
});
}
EnterpriseCacheSessionDAO
从父类继承的持久化操作方法(doReadSession
、doUpdate
和doDelete
)都是空实现,也就说EnterpriseCacheSessionDAO
是没有实现持久化操作的,仅仅只是简单的提供了缓存实现。当然我们可以继承EnterpriseCacheSessionDAO
,重写doReadSession
、doUpdate
和doDelete
方法来实现持久化操作。
// AbstractSessionDAO中doReadSession的重写
protected Session doReadSession(Serializable sessionId) {
return null;
}
// CachingSessionDAO中doUpdate的重写
protected void doUpdate(Session session) {
}
// CachingSessionDAO中doDelete的重写
protected void doDelete(Session session) {
}
总结
SessionDAO
定义了从持久层操作session的标准;AbstractSessionDAO
提供了SessionDAO
的基础实现,如生成会话ID等;CachingSessionDAO
提供了对开发者透明的session缓存的功能,只需要设置相应的 CacheManager
即可;MemorySessionDAO
直接在内存中进行session维护;而EnterpriseCacheSessionDAO
提供了缓存功能的session维护,默认情况下使用 MapCache
实现,内部使用ConcurrentHashMap
保存缓存的会话。
因为shiro不知道我们需要将session持久化到哪里,所以只提供了MemorySessionDAO
持久化到内存。