1. 程式人生 > >3.CacheManager(shiro快取管理)

3.CacheManager(shiro快取管理)

上一章我們講了SecurityManager,接下來的章節順序是根據SecurityManager的依次實現類的依賴元件來講。

在講cacheManager前,我們先了解下Cache。
Cache有效的儲存臨時物件來提升應用的效能。由於Cache不屬於安全框架的核心功能,所以shiro本身並沒有完全實現Cache機制。Cache介面相當於底層的快取框架的頂層介面,shiro的一切的快取操作都與這個Cache頂層介面操作,而底層的實現可以是任何Cache例項(JAche、Ehcache、OSCache、JBossCache..)。

這裡寫圖片描述

cacheManager維護了Cache例項的生命週期,它和Cache一樣,只是shiro的快取框架的頂層介面,具體底層實現可以是任意的。

這裡寫圖片描述

1.CacheManager

//CacheManager管理Cache的宣告週期
public interface CacheManager {

    //根據指定的名字獲取Cache(為什麼需要根據名字?因為CacheManager就相當於一個大容器,只管理Cache<K,V>,而這個Cache<K,V>是個小容器,根據鍵可以取到對應的值。在shiro裡面,AuthenticationInfo(使用者登陸資訊),AuthorizationInfo(授權資訊)生成一個Cache,然後根據唯一的名字存入到CacheManager裡),如果不存在,則建立個Cache存入到CacheManager裡然後返回。
public <K, V> Cache<K, V> getCache(String name) throws CacheException; }

2.AbstractCacheManager

//CacheManager的簡單的抽象實現,該實現把Cache都交由ConcurrentMap來管理。
public abstract class AbstractCacheManager implements CacheManager, Destroyable {


    private final ConcurrentMap<String, Cache> caches;


    public
AbstractCacheManager() { this.caches = new ConcurrentHashMap<String, Cache>(); } //根據名字獲取Cache public <K, V> Cache<K, V> getCache(String name) throws IllegalArgumentException, CacheException { if (!StringUtils.hasText(name)) { throw new IllegalArgumentException("Cache name cannot be null or empty."); } Cache cache; //首先根據名字獲取從ConcurrentMap裡獲取cache cache = caches.get(name); if (cache == null) { //如果cache為null的話,則生成個cache存入ConcurrentMap再返回 cache = createCache(name); //putIfAbsent是ConcurrentMap的一個原子性操作,如果已經存在則不新增。 Cache existing = caches.putIfAbsent(name, cache); if (existing != null) { cache = existing; } } //noinspection unchecked return cache; } //建立一個Cache,留待子類實現 protected abstract Cache createCache(String name) throws CacheException; //銷燬CacheManager public void destroy() throws Exception { while (!caches.isEmpty()) { for (Cache cache : caches.values()) { //LifecycleUtils.destroy()裡面,會自動判斷該cache是否實現了Destroyable,是的話,執行Destroyable的destroy方法。如果是Collection,迭代元素判斷是否實現Destroyable,如果實現,則執行Destroyable的destroy方法 LifecycleUtils.destroy(cache); } caches.clear(); } } public String toString() { Collection<Cache> values = caches.values(); StringBuilder sb = new StringBuilder(getClass().getSimpleName()) .append(" with ") .append(caches.size()) .append(" cache(s)): ["); int i = 0; for (Cache cache : values) { if (i > 0) { sb.append(", "); } sb.append(cache.toString()); i++; } sb.append("]"); return sb.toString(); } }

3.MemoryConstrainedCacheManager

//繼承AbstractCacheManager
public class MemoryConstrainedCacheManager extends AbstractCacheManager {

    //該類返回一個MapCache(實現Cache)。
    @Override
    protected Cache createCache(String name) {
        //SoftHashMap實現了Map。該實現Map不會引起記憶體洩露,因為裡面引用了弱引用和強引用。預設的強引用是100個。超出了則為弱引用,垃圾回收器可回收這些弱引用,在需要記憶體的時候。
        return new MapCache<Object, Object>(name, new SoftHashMap<Object, Object>());
    }
}

下面講的是Ehcache的CacheManager的實現
1.EhCacheManager

//EhCacheManager利用了Ehcache框架來實現所有的快取功能,可以手動配置net.sf.ehcache.CacheManager和ehcache.xml路徑。
public class EhCacheManager implements CacheManager, Initializable, Destroyable {


    private static final Logger log = LoggerFactory.getLogger(EhCacheManager.class);

    //屬於ehcache框架的CacheManager,shiro的EhCacheManager僅僅只是代理它實現快取功能。
    protected net.sf.ehcache.CacheManager manager;

    //表面這個manager是由本例項構造的,而非通過外部注入進來的。需要在銷燬的時候執行銷燬方法
    private boolean cacheManagerImplicitlyCreated = false;

    // ehcache CacheManager配置檔案路徑
    private String cacheManagerConfigFile = "classpath:org/apache/shiro/cache/ehcache/ehcache.xml";


    public EhCacheManager() {
    }


    public net.sf.ehcache.CacheManager getCacheManager() {
        return manager;
    }

    //設定ehcache的CacheManager
    public void setCacheManager(net.sf.ehcache.CacheManager manager) {
        this.manager = manager;
    }

    //如果ehcache的CacheManager沒有初始化,則獲取 ehcache CacheManager的配置檔案路徑,來初始化ehcache的CacheManager.路徑載入方式可以是:classpath:、url:file:
    public String getCacheManagerConfigFile() {
        return this.cacheManagerConfigFile;
    }

    //classpathLocation可以是:classpath:、url:、file:字首開頭的載入方式
    public void setCacheManagerConfigFile(String classpathLocation) {
        this.cacheManagerConfigFile = classpathLocation;
    }

    //使用 ResourceUtils.getInputStreamForPath(configFile)來載入配置檔案到InputStream
    protected InputStream getCacheManagerConfigFileInputStream() {
        String configFile = getCacheManagerConfigFile();
        try {
            return ResourceUtils.getInputStreamForPath(configFile);
        } catch (IOException e) {
            throw new ConfigurationException("Unable to obtain input stream for cacheManagerConfigFile [" +
                    configFile + "]", e);
        }
    }

    //根據名字獲取Cache
    public final <K, V> Cache<K, V> getCache(String name) throws CacheException {

        if (log.isTraceEnabled()) {
            log.trace("Acquiring EhCache instance named [" + name + "]");
        }

        try {
        //從ehcache框架的CacheManager中根據名字獲取ehcache框架的Cache
            net.sf.ehcache.Ehcache cache = ensureCacheManager().getEhcache(name);
            if (cache == null) {
                if (log.isInfoEnabled()) {
                    log.info("Cache with name '{}' does not yet exist.  Creating now.", name);
                }
        //根據名字新增cache
                this.manager.addCache(name);
        //重新根據名字獲取cache
                cache = manager.getCache(name);

                if (log.isInfoEnabled()) {
                    log.info("Added EhCache named [" + name + "]");
                }
            } else {
                if (log.isInfoEnabled()) {
                    log.info("Using existing EHCache named [" + cache.getName() + "]");
                }
            }
        //shiro的EhCache包裝了ehcache框架的Cache,然後返回
            return new EhCache<K, V>(cache);
        } catch (net.sf.ehcache.CacheException e) {
            throw new CacheException(e);
        }
    }

    //下面這個我也不是很理解,希望理解的朋友能告知一二。特別是這句(fail-safe expunges cached objects after 2 minutes, something not desirable for Shiro sessions)。大概意思就是講,如果沒有通過setCacheManager 注入CacheManager,那麼該方法就會載入ehcache的配置檔案路徑初始化一個CacheManager
    /**
     * Initializes this instance.
     * <p/>
     * If a {@link #setCacheManager CacheManager} has been
     * explicitly set (e.g. via Dependency Injection or programatically) prior to calling this
     * method, this method does nothing.
     * <p/>
     * However, if no {@code CacheManager} has been set, the default Ehcache singleton will be initialized, where
     * Ehcache will look for an {@code ehcache.xml} file at the root of the classpath.  If one is not found,
     * Ehcache will use its own failsafe configuration file.
     * <p/>
     * Because Shiro cannot use the failsafe defaults (fail-safe expunges cached objects after 2 minutes,
     * something not desirable for Shiro sessions), this class manages an internal default configuration for
     * this case.
     *
     * @throws org.apache.shiro.cache.CacheException
     *          if there are any CacheExceptions thrown by EhCache.
     * @see net.sf.ehcache.CacheManager#create
     */
    public final void init() throws CacheException {
        ensureCacheManager();
    }

    private net.sf.ehcache.CacheManager ensureCacheManager() {
        try {
            if (this.manager == null) {
                if (log.isDebugEnabled()) {
                    log.debug("cacheManager property not set.  Constructing CacheManager instance... ");
                }
                //because we need to know if we need to destroy the CacheManager instance - using the static call。
                //上面這一句我也不理解。其他幾句大概意思就是說,通過CacheManager 的構造方法,得到的例項不是CacheManager裡的單例的。我們不知道CacheManager 是否會shutting 它的單例。但是我們通過使用構造EhCacheManager內部的CacheManager,那麼EhCacheManager總會shutting EhCacheManager內部的CacheManager。
                //using the CacheManager constructor, the resulting instance is _not_ a VM singleton
                //(as would be the case by calling CacheManager.getInstance().  We do not use the getInstance here
                //because we need to know if we need to destroy the CacheManager instance - using the static call,
                //we don't know which component is responsible for shutting it down.  By using a single EhCacheManager,
                //it will always know to shut down the instance if it was responsible for creating it.
                this.manager = new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream());
                if (log.isTraceEnabled()) {
                    log.trace("instantiated Ehcache CacheManager instance.");
                }
                cacheManagerImplicitlyCreated = true;
                if (log.isDebugEnabled()) {
                    log.debug("implicit cacheManager created successfully.");
                }
            }
            return this.manager;
        } catch (Exception e) {
            throw new CacheException(e);
        }
    }

   //如果CacheManager是由EhCacheManager內部構造的,則會執行CacheManager銷燬。如果該CacheManager是通過外部元件注入的,那麼該外部元件也應該負責銷燬CacheManager。
    public void destroy() {
        if (cacheManagerImplicitlyCreated) {
            try {
                net.sf.ehcache.CacheManager cacheMgr = getCacheManager();
                cacheMgr.shutdown();
            } catch (Exception e) {
                if (log.isWarnEnabled()) {
                    log.warn("Unable to cleanly shutdown implicitly created CacheManager instance.  " +
                            "Ignoring (shutting down)...");
                }
            }
            cacheManagerImplicitlyCreated = false;
        }
    }
}