美文网首页java全栈开发
Shiro 中的 SessionDAO

Shiro 中的 SessionDAO

作者: JSON_NULL | 来源:发表于2019-02-13 12:14 被阅读6次

    SessionDAO是用于session持久化的,SessionManager只是负责session的管理,持久化的工作是由SessionDAO完成的。

    SessionDAO的继承结构

    SessionDAO 接口

    SessionDAO接口是Shiro中所有SessionDAO的顶级接口,给出了从持久层(一般而言是关系型数据库)操作session的标准。方法如下:

    1. Serializable create(Session session); 插入一个新的Session到持久化设备。
    2. Session readSession(Serializable sessionId) throws UnknownSessionException; 根据会话ID获取会话。
    3. void update(Session session) throws UnknownSessionException; 更新session; 如更新session最后访问时间/停止会话/设置超时时间/设置移除属性等会调用。
    4. 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实现了SessionDaocreate方法;并把具体的创建并持久化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实现了SessionDaoreadSession方法;并把具体的从持久化设备获取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仅是提供了缓存功能,但并没有实现缓存功能,具体的缓存功能和缓存方案还需要由子类来完成。

    1. 重写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;
    }
    
    1. 重写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;
    }
    
    1. 实现了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抽象方法方法由子类来实现。

    1. 实现了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抽象方法方法由子类来实现。

    1. 实现了SessionDAO中的getActiveSessions方法如下:
    /**
     * SessionDAO中getActiveSessions的实现
     * 获取所有的存活的session
     */
    public Collection<Session> getActiveSessions() {
        Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
        if (cache != null) {
            return cache.values();
        } else {
            return Collections.emptySet();
        }
    }
    

    MemorySessionDAO 实现类

    MemorySessionDAOSessionDAO的简单内存实现。将session保存在内存中,存储结构是ConcurrentHashMap;项目中基本上用不到,就不详细说明了。

    EnterpriseCacheSessionDAO 实现类

    EnterpriseCacheSessionDAO继承自CachingSessionDAO,在构造器中设置了默认的缓存管理器(AbstractCacheManager)和默认的缓存实例(MapCache),实现了缓存效果。代码如下:

    public EnterpriseCacheSessionDAO() {
        // 设置默认缓存器,并实例化MapCache作为cache实例
        setCacheManager(new AbstractCacheManager() {
            @Override
            protected Cache<Serializable, Session> createCache(String name) throws CacheException {
                return new MapCache<Serializable, Session>(name, 
                    new ConcurrentHashMap<Serializable, Session>());
            }
        });
    }
    

    EnterpriseCacheSessionDAO从父类继承的持久化操作方法(doReadSessiondoUpdatedoDelete)都是空实现,也就说EnterpriseCacheSessionDAO是没有实现持久化操作的,仅仅只是简单的提供了缓存实现。当然我们可以继承EnterpriseCacheSessionDAO,重写doReadSessiondoUpdatedoDelete方法来实现持久化操作。

    // 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持久化到内存。

    相关文章

      网友评论

        本文标题:Shiro 中的 SessionDAO

        本文链接:https://www.haomeiwen.com/subject/fxhcsqtx.html