3.CacheManager(shiro快取管理)
阿新 • • 發佈:2019-02-14
上一章我們講了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;
}
}
}