缓存配置oscache.properties
cache.memory | 是否使用内存缓存; true 或 false。默认为true; 如设置为false,那cache只能缓存到数据库或硬盘中。 |
cache.capacity | 缓存的最大数量。默认是不限制,cache不会移走任何缓存内容。负数也被视不限制。 |
cache.algorithm | 运算规则。为了使用规则,cache.capacity必须是指定的。有下面三种规则: com.opensymphony.oscache.base.algorithm.LRUCache: last in first out(最后插入的最先调用)。 com.opensymphony.oscache.base.algorithm.FIFOCache: first int first out(最先插入的最先调用)。 com.opensymphony.oscache.base.algorithm.UnlimitedCache: cache中的内容将永远不会被丢弃。 默认选项 如果不指定cache.capacity,它将被设为默认选项即不限制。 |
cache.blocking | 是否同步。true 或者 false。一般设为true,避免读取脏数据。 |
cache.unlimited.disk | 指定硬盘缓存是否要作限制。默认false,disk cache capacity 和cache.capacity的值相同。 |
cache.persistence.class | 指定类是被持久化缓存的类。class必须实现PersistenceListener接口。 作为硬盘持久,可以实现HashDiskPersistenceListener接口。 注意:HashDiskPersistenceListener 和 DiskPersistenceListener 需要设定硬盘路径:cache.path。 |
cache.path | 硬盘缓存的路径。目录如果不存在将被建立。注意oscache应该要有权限写文件系统。 |
cache.persistence.overflow.only | 是否只有在内存不足的情况下才使用硬盘缓存。默认值false。 如果内存cache被允许的话(cache.memore=true),推荐设置为true。 |
cache.event.listeners | 用逗号隔开的class名列表,默认是不配置任何class的。 每个class必须实现以下接口中的一个或者几个CacheEntryEventListener:接收cache add/update/flush/remove事件 CacheMapAccessEventListener :接收cache访问事件。这个可以让你跟踪cache怎么工作。 com.opensymphony.oscache.plugins.clustersupport.BroadcastingCacheEventListener : 分布式的监听器。可以广播到局域网内的其他cache实例。 com.opensymphony.oscache.extra.CacheEntryEventListenerImpl :一个简单的监听器。在cache的生命周期中记录所有entry的事件。 com.opensymphony.oscache.extra.CacheMapAccessEventListenerImpl : 记录count of cache map events(cache hits,misses and state hits) |
cache.key |
在application 和 session的作用域时用于标识cache 对象的。默认值:"__oscache_cache" 如果代码中需要用到默认值时可以通使用com.opensymphony.oscache.base.Const.DEFAULT_CACHE_KEY 来取得 |
cache.use.host.domain.in.key | 当配置多个服务器时,想通过服备器名称自动生成cache key时,可将此属性设为true. 默认值为false |
核心类和概念
角色 | 类 | 作用 |
cache factory
缓存管理
|
AbstractCacheAdministrator
GeneralCacheAdministrator
ServletCacheAdministrator
|
生产Cache,同时管理用户配置的config,配置监听器列表和Cache的绑定。
负责获得 Cache Proxy ,兼有一些对 Cache Proxy 的管理功能。
子类ServletCacheAdministrator关联了一个ServletContext,以实现在Web容器中的存储和传值
|
cache proxy
缓存接口
|
Cache
ServletCache
|
OSCache缓存管理的核心,也是cache map的存放场所。
Cache Map 的代理,它主要负责从 Cache Map 中取得 / 存储指定的缓存对象,
如果缓存对象过期,那么就将缓存刷新,并向指定的监听者发送存 / 取事件。
子类ServletCache引入了一个scope的概念,用以管理不同的可见性缓存,存在application session级别。
|
cache map
缓存
|
AbstractConcurrentReadCache
FIFOCache ,LRUCache,UnlimitedCache
|
缓存存储map。存储了所有的缓存实体,是一个 OSCache 专有的 Map 实现,
根据指定的算法清除缓存,以及将缓存持久化到磁盘中。
|
cache entry
缓存条目
|
CacheEntry |
map中存储的每一项。其内部包含了缓存条目的创建、修改时间,存储的key、value,group等属性
缓存对象的包装实体,它包装了缓存对象和刷新策略。
|
cache map 对 cache entry的管理
EntryUpdateState是cache entry当前所处状态的表示,OSCache尽量避免了使用synchronize,引入了许多状态参量。
典型应用场景
典型的“缓存对象”场景是:
应用调用Cache Factory获得Cache Proxy,然后应用将要缓存的对象以及刷新策略通过Cache Proxy存储到Cache Map中,并通知各个Listener。
典型的“取得缓存对象”的场景是:
应用调用 Cache Factory 获得 Cache Proxy ,然后给 Cache Proxy 的相应方法传入要获得的缓存对象的 key ,
Cache Proxy 会根据指定的刷新策略判断缓存是否过期,如果缓存没有过期,则返回缓存对象,如果缓存过期,则刷新缓存,
并向应用层抛出需要刷新的异常NRE,应用如果收到此异常,将重新计算内容并将内容缓存。
Cache Proxy 会根据指定的刷新策略判断缓存是否过期,如果缓存没有过期,则返回缓存对象,如果缓存过期,则刷新缓存,
并向应用层抛出需要刷新的异常NRE,应用如果收到此异常,将重新计算内容并将内容缓存。
在缓存条目过期或者不存在的时候都会抛出NeedsRefreshException。
当这个异常出现,相应的cache map的key会被锁住,并且要访问它的所有其他线程都被block住了,
所以,这个时候一定要调用putInCache或者cancelUpdate,千万不能遗漏,否则就是一个死锁。
当这个异常出现,相应的cache map的key会被锁住,并且要访问它的所有其他线程都被block住了,
所以,这个时候一定要调用putInCache或者cancelUpdate,千万不能遗漏,否则就是一个死锁。
缓存刷新处理
在catch(NeedRefreshException)中进行 putInCache,又在putInCache的catch(Exception)中做cancelUpdate,因为我们不希望看到一个线程在 getFromCache的位置一直阻塞下去。
GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
admin.putInCache("myKey", "HelloWorld"); ①
String myKey = "myKey";
String myValue;
int myRefreshPeriod = 1000;
try {
// Get from the cache
myValue = (String) admin.getFromCache(myKey, myRefreshPeriod); ②
} catch (NeedsRefreshException nre) {
try {
// Get the value (probably from the database)
myValue = "This is the content retrieved." ;
// Store in the cache
admin.putInCache(myKey, myValue); ③
} catch (Exception ex) {
// We have the current content if we want fail-over.
myValue = (String) nre.getCacheContent();
// It is essential that cancelUpdate is called if the
// cached content is not rebuilt
admin.cancelUpdate(myKey);
}
}
|
②从对象缓存map中取出myKey对应的对象,两种情况可能发生:
1 如果对象存在(先前putInCache①)且没有过期,getFromCache正常返回。
2 如果对应的对象不存在或者过期,需要刷新缓存:
1) 请求的线程第一个探测到对象过期,抛给client一个NRE,
提示client需要对数据进行刷新putInCache③。
2) 如果请求的线程并非第一个探测到对象不存在:
A.之前探测到的线程没有进行刷新处理,直接阻塞。
B.之前探测到的线程进行了刷新处理,抛出NRE。
3) 如果请求的线程并非第一个探测到对象过期:
A.之前探测到的线程没有进行刷新处理,直接阻塞。
B.之前探测到的线程进行了刷新处理:
I.如果blocking=false 抛出NRE,采取补救措施取可能过期内容
II.如果blocking=true,那么该线程会在此阻塞,
直到putInCache/cancelUpdate在另一个线程中被调用。
|
源码分析
Cache管理类
// 私有化Cache实例,不直接操作Cache实例,而是通过管理类对缓存管理:put,get,flush。当然也可以通过getCache获取Cache实例对Cache操作
private Cache applicationCache = null;
public GeneralCacheAdministrator(Properties p) {
super (p); //调用父类AbstractCacheAdministrator的构造函数
createCache();
}
protected AbstractCacheAdministrator(Properties p) {
loadProps(p); //加载配置文件,封装到对象 Config里(通过 Config获取配置信息)
initCacheParameters(); //初始化参数,将 Config获取到的配置信息赋值给几个重要的属性,目的是在类或子类中可以直接使用已经赋值好的变量
}
private void createCache () {
applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isOverflowPersistence(), isBlocking(), algorithmClass , cacheCapacity );
//创建完Cache实例后,将配置文件配置的监听器应用到Cache上
configureStandardListeners( applicationCache);
}
|
getFromCache
读取缓存数据,指的是从CacheMap[缓存]中获取对应的CacheEntry[缓存条目],再获取CacheEntry的内容[缓存对象]。
updateStates:map对象。它提供了cache中的所有被同步访问的cacheEntry对象的状态集合,用来协调并发访问(修改/读取)同一个缓存条目。
key就是cache中该cacheEntry对象对应的key。value是updatestate 对象。
OSCache采用引用计数状态量机制,解决了多线程并发访问缓存的问题,同时,没有任何语句锁住整个cache map,在高并发的情况下不会有太大的性能损失。
当一个过期的缓存条目被请求,或者观察到缓存未命中,cache会检查这个map
当一条缓存条目正在被另一个线程更新,那么有两种策略,根据配置项cache.blocking的配置,
要么等待更新完成(阻塞策略),要么返回已经过时的缓存内容(非阻塞策略)。
为了避免数据争用(并发读取),map里面的值在某线程操作的过程中不能消失,
因此updateStates实际的作用是显式引用计数(每一个updateState里面都有一个计数器),
在所有线程都对指定的key完成存取和更新以后,map的这条entry才能被清除掉。
updateStates存储的是所有 处于更新状态的(UPDATE_IN_PROGRESS) 缓存条目,
也就是说如果某一个缓存条目没有被更新(NOT_YET_UPDATING),则不会存放在这个map里。
或者当前缓存条目更新完毕(UPDATE_COMPLETE),则被从这个map里踢除掉,因为不再处于正在更新的状态!
当一条缓存条目正在被另一个线程更新,那么有两种策略,根据配置项cache.blocking的配置,
要么等待更新完成(阻塞策略),要么返回已经过时的缓存内容(非阻塞策略)。
为了避免数据争用(并发读取),map里面的值在某线程操作的过程中不能消失,
因此updateStates实际的作用是显式引用计数(每一个updateState里面都有一个计数器),
在所有线程都对指定的key完成存取和更新以后,map的这条entry才能被清除掉。
updateStates存储的是所有 处于更新状态的(UPDATE_IN_PROGRESS) 缓存条目,
也就是说如果某一个缓存条目没有被更新(NOT_YET_UPDATING),则不会存放在这个map里。
或者当前缓存条目更新完毕(UPDATE_COMPLETE),则被从这个map里踢除掉,因为不再处于正在更新的状态!
根据key读取CacheMap中的缓存对象
[1] 缓存中不存在key对应的缓存条目,都没有怎么获取?
[2] key对应的updateState已经存在,说明获取该key对象的线程又多了一个
[3] updateState为等待更新或被取消更新,设置状态为正在更新,并将线程引用计数+1,抛出NRE异常,提示客户端刷新缓存
[4] 缓存过期,如果缓存内容为空,访问类型为未命中,不为空,访问类型为命中过期数据,都会抛出NRE异常
[5] 缓存数据存在,如果没有过期,直接返回。如果过期,当其他线程正在更新,如果配置blocking=true则等待更新完成并获取最新数据,否则直接返回过期数据。
[6] 其他线程正在更新缓存,但是又取消了更新,当前线程设置状态为正在更新,并将线程引用计数+1,抛出NRE异常,提示客户端刷新缓存
[7] 同步块结束后,将[2]读取缓存的线程计数-1,如果没有线程引用updateState,则释放updateState对象
[8] 客户端捕获到NRE异常,刷新缓存,可以调用putInCache()或cancleUpdate(),将引用计数-1,释放updateState对象
读取流程
判断缓存是否过期,如果过期,那么会从更新状态缓存中查找更新状态,如果没查到那么创建一个,如果已经存在了,将引用计数+1。 ->getUpdateState(key)
如果缓存还没有进行更新,那么将更新状态设置为UPDATE_IN_PROGRESS,并且再次将引用计数+1,此时:
(1) 在设定完更新状态后,读取缓存的线程会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。 -> finally里
(2) 如果其他线程在更新缓存的时候会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。
这样,通过上边的两个过程,就将引用计数变为0了。
如果缓存还没有进行更新,那么将更新状态设置为UPDATE_IN_PROGRESS,并且再次将引用计数+1,此时:
(1) 在设定完更新状态后,读取缓存的线程会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。 -> finally里
(2) 如果其他线程在更新缓存的时候会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。
这样,通过上边的两个过程,就将引用计数变为0了。
public Object getFromCache(String key, int refreshPeriod, String cronExpiry) throws NeedsRefreshException {
// 先尝试在缓存CacheMap中查找。和putInCache一样,如果存在则返回缓存条目,
// 不存在则新建一个缓存条目并返回.因为缓存CacheMap中不存在这个缓存条目,缓存对象也为空[1]
CacheEntry cacheEntry = this.getCacheEntry(key, null, null);
Object content = cacheEntry.getContent(); // 缓存内容,实际缓存的对象[5]
CacheMapAccessEventType accessEventType = CacheMapAccessEventType. HIT; // 缓存访问事件类型,默认是"缓存命中"
boolean reload = false; //是否重新加载,如果另外的线程正在更新,等待更新完毕后,reload=true,重加获取缓存里的对象
// Check if this entry has expired or has not yet been added to the cache.If so, 缓存条目过期/还没加入到缓存中[1]
// we need to decide whether to block, serve stale content or throw a NeedsRefreshException 阻塞,获取过期数据,抛出NRE
if ( this.isStale(cacheEntry, refreshPeriod, cronExpiry)) {
// Get access to the EntryUpdateState instance and increment the usage count during the potential sleep
// 检测updateStates中是否存在该key,如果没有,则为该key新建一个EntryUpdateState对象并将其put到updateStates中;
// 如果存在该key,则需要为该对象的同时被访问的线程计数+1,表示读取该key对象的线程又多了一个[2:+1]
EntryUpdateState updateState = getUpdateState(key);
try {
// 锁住updateState,即对updateState进行同步
synchronized (updateState) {
// A.若状态为"等待更新",则该对象尚未被其他线程访问
// A.若状态为"被取消",则表明已有其他的线程对该CacheEntry对象进行访问,但当前非"正在被更新"的状态
if (updateState.isAwaitingUpdate() || updateState.isCancelled()) {
// No one else is currently updating this entry - grab ownership
// 将更新状态设置为正在更新UPDATE_IN_PROGRESS,再次将线程引用计数+1[3:+1]
// 只是更新状态并没有真正更新,再说了这里只是读取操作所以肯定不会更新
// 先了解下此时状态:等待更新,实际是等待被更新(被动的,读取操作显然不会主动更新,而是客户端主动更新)
// 这里引用计数+1,只是提示客户端更新缓存条目,当客户端更新完缓存条目,会将引用计数-1,这样就完成了一次存取操作
updateState.startUpdate();
// 新建缓存条目,还没放入缓存中[1],缓存的对象内容为空,因此将此次访问记录为未命中(缓存中都没有数据,显然没有命中)
if (cacheEntry.isNew()) {
accessEventType = CacheMapAccessEventType. MISS;
}
// 若对象内容不为空,则表明该次访问的数据已经过期,记录为命中过期数据(缓存中有数据但是过期了)
// 进入同步块时判断缓存条目是否过期当数据有效时,执行到这里,既然不是新建的缓存条目,就一定是过期的数据
else {
accessEventType = CacheMapAccessEventType. STALE_HIT;
}
// 这两种类型,在后面的处理中[4],都会抛出NRE异常,提示客户端刷新缓存。未命中和命中过期数据都不是我们想要的。
}
// B.若状态为"正在更新",表明有其他线程正在对该对象进行更新
else if (updateState.isUpdating()) {
// Another thread is already updating the cache. We block if this is a new entry, or blocking mode is enabled.
// Either putInCache() or cancelUpdate() can cause this thread to resume.
// 在配置为阻塞策略的情况下,或这是一个新建的缓存条目[1],都会阻塞,等待更新完成后notify
// 1:其他线程正在对该对象进行更新,是否需要等待其他线程更新完毕读取最新数据,或者直接返回过期数据[5]
// 2:其他线程取消对该对象更新,此时需要抛出NRE异常;
// 3:其他线程对该对象的更新完毕,此时可以返回最新数据。
if (cacheEntry.isNew() || blocking) {
do {
try {
// 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去(交给正在更新该缓存条目的线程),然后处于等待状态
// 当更新缓存条目的线程更新完毕,即在putInCache()的completeUpdate(key)中调用updateState.notifyAll()
// notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行,
// 即当前线程继续判断updateState的状态,两种状态:更新被取消或更新完成
updateState.wait();
} catch (InterruptedException e) {
}
} while (updateState.isUpdating());
// 如果其他正在更新的线程取消了更新(虽然你说你正在更新但你又取消这个操作),那么缓存仍然是过期的,执行和前面分支相同的操作
if (updateState.isCancelled()) {
// The updating thread cancelled the update, let this one have a go. 将更新操作让给当前的线程来更新
// This increments the usage count for this EntryUpdateState instance 引用计数+1[6:+1]
updateState.startUpdate();
if (cacheEntry.isNew()) {
accessEventType = CacheMapAccessEventType. MISS;
} else {
accessEventType = CacheMapAccessEventType.STALE_HIT ;
}
// 抛出NRE异常[4]
}
// 如果没取消更新的话,那么这时updateState的状态应该是complete,即其他线程更新完毕,此时可以返回最新数据
// 其他线程在更新完毕时,会将同步访问计数-1,表明一个线程对该对象的访问结束。
else if (updateState.isComplete()) {
reload = true;
} else {
log.error("Invalid update state for cache entry " + key);
}
}
// 如果缓存中有数据(此时是过期的了),且blocking=false,下面的逻辑除了finally外都不执行,返回过期数据content[5]
}
// C.若状态为"完成更新",则表明有其他线程调用了completeUpdate(key)将该对象的状态改为"完成更新"
// 数据是过期的,在进入同步块后发现另外的线程已将数据更新完毕,当前线程不需要做任何操作,只需要从缓存中获取最新的数据
else {
reload = true;
}
}
} finally {
// Make sure we release the usage count for this EntryUpdateState since we don't use it anymore.
// If the current thread started the update, then the counter was increased by one in startUpdate()
// 在设定完更新状态后,读取缓存的线程会将引用计数-1[7:-1]对应[2:+1],
// 如果没有线程此时再引用了,updateStates就会移除此项updateState,释放updateState对象
releaseUpdateState(updateState, key);
}
}
// If reload is true then another thread must have successfully rebuilt the cache entry
// 其他线程已经重建缓存条目了,可以重新载入缓存中的数据了
if (reload) {
cacheEntry = (CacheEntry) cacheMap.get(key);
if (cacheEntry != null) {
content = cacheEntry.getContent();
} else {
log.error("Could not reload cache entry after waiting for it to be rebuilt");
}
}
dispatchCacheMapAccessEvent(accessEventType, cacheEntry, null); // 发送缓存事件
// If we didn't end up getting a hit then we need to throw a NRE 如果没有命中数据(未命中或命中过期),抛给客户端需要刷新数据的异常
// 上面updateState.startUpdate()提示客户端刷新缓存,都会执行到这里[4]
if (accessEventType != CacheMapAccessEventType. HIT) {
throw new NeedsRefreshException(content);
}
// 当抛出异常时,没有将同步访问计数减一,因此在设置状态为“正在更新”时增加的计数不会被减去,该 updateState对象将被保留在 updateStates中
// 在捕获NRE异常的地方一定要调用completeUpdate或cancleUpdate方法,将计数-1[8:-1]对应[3:+1]或[6:+1],这样就会释放 updateState对象
return content;
}
|
putInCache写入缓存
public void putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy, String origin) {
// 首先查找这个key对应的缓存条目在缓存中是否已经存在,没有就新建CacheEntry,注意此时缓存对象的内容还没有设置
CacheEntry cacheEntry = this.getCacheEntry(key, policy, origin);
// 判断是否是新创建的缓存
boolean isNewEntry = cacheEntry.isNew();
// [CACHE-118] If we have an existing entry, create a new CacheEntry so we can still access the old one later
// 如果不是新的缓存也会新建一个CacheEntry,因为老的缓存值在后边也能访问到
if (!isNewEntry) {
cacheEntry = new CacheEntry(key, policy);
}
cacheEntry.setContent(content);
cacheEntry.setGroups(groups);
cacheMap.put(key, cacheEntry); // 放入缓存:将content放入CacheEntry,将CacheEntry放入CacheMap
// Signal to any threads waiting on this update that it's now ready for them in the cache!
// 通知其他在等待值的线程结束等待,现在该key对应的缓存条目已经准备好了,其他线程可以获取最新的缓存内容了
completeUpdate(key);
// 针对缓存事件的listener发送事件
if ( listenerList.getListenerCount() > 0) {
CacheEntryEvent event = new CacheEntryEvent(this, cacheEntry, origin);
if (isNewEntry) {
dispatchCacheEntryEvent(CacheEntryEventType. ENTRY_ADDED, event); // 新添加缓存
} else {
dispatchCacheEntryEvent(CacheEntryEventType. ENTRY_UPDATED, event); // 更新缓存
}
}
}
|
Web应用集成
缓存对象:直接调用API的接口即可 缓存部分页面: 复制oscache.tld到WEB-INF下,在web.xml添加
<taglib>
<taglib-uri>oscache</taglib-uri>
<taglib-location>/WEB-INF/oscache.tld</taglib-location>
</taglib>
在jsp页面使用标签<%@ taglib uri="oscache" prefix="cache"%>使用Oscache的标签<oscache></oscache>来进行页面的局部缓存 缓存整个页面:用CashFilter实现页面级缓存,可缓存单个文件、缓存URL pattern和自己设定缓存属性的缓存。 主要用于对web应用中的某些动态页面进行缓存,尤其是那些需要生成PDF格式文件/报表、图片文件等的页面, 不仅减少了数据库的交互、减少数据库服务器的压力,而且对于减少web服务器的性能消耗有很显著的效果。 在web.xml中进行配置来决定缓存哪一个或者一组页面,而且还可以设置缓存的相关属性。 [注] 只有客户访问时返回http头信息中代码为200(也就是访问已经成功)的页面信息才能够被缓存。
<filter><filter-name>CacheFilter</filter-name> <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class> <init-param> <param-name>time</param-name> <param-value>600</param-value> </init-param> <init-param> <param-name>scope</param-name> <param-value>session</param-value> </init-param> </filter> <filter-mapping> <filter-name>CacheFilter</filter-name> <url-pattern>*.jsp</url-pattern> <!-- 对所有jsp页面内容进行缓存--> </filter-mapping> |
cache标签(缓存部分页面)
第一次请求到达时,标签中的内容被处理并且缓存起来,当下一个请求到达时,缓存系统会检查这部分内容的缓存是否已经失效,
如果符合下面四项中的任何一项,被缓存的内容视为已经失效,这时被缓存的内容将被重新处理并且返回处理过后的信息,
如果被缓存的内容没有失效,那么返回给用户的将是缓存中的信息。主要是以下几项:
(1)缓存时间超过了cache标签设置的time或者duration属性规定的超时时间
(2)cron属性规定的时间比缓存信息的开始时间更晚
(3)标签中缓存的内容在缓存后又被重新刷新过
(4)其他缓存超期设定
(2)cron属性规定的时间比缓存信息的开始时间更晚
(3)标签中缓存的内容在缓存后又被重新刷新过
(4)其他缓存超期设定
key
|
标识缓存内容的关键词。在指定的作用范围内必须是唯一的。默认的key是被访问页面的URI和后面的请求字符串。
|
scope | 缓存发生作用的范围,application(默认)或者session。 |
time |
缓存内容的时间段,单位是秒,默认是3600秒,如果设定一个负值,那么这部分被缓存的内容将永远不过期。
|
duration |
指定缓存内容失效的时间,是相对time的另一个选择,可以使用简单日期格式或者符合USO-8601的日期格式。如:duration='PT5M' duration='5s'等。
|
cron | 指定缓存内容失效表达式 |
refresh |
false/true。如果refresh属性设置为true,不管其他的属性是否符合条件,这部分被缓存的内容都将被更新,这给编程者一种选择,决定什么时候必须刷新。
|
mode |
如果不希望被缓存的内容增加到给用户的响应中,可以设置mode属性为"silent"。
此时被缓存的部分不在页面上显示,而其它任意的mode属性值都会将缓存的部分显示到页面上。
|
groups |
指定当前cache标签所属的组,可使用“,”分割组名。这样就可以对缓存项进行分组了。
如果缓存项依赖于应用的其它部分或其它数据,分组就有了用武之地——当这种依赖改变时(刷新相关的组),这个组的所有缓存项都将过期。
|
language |
使用ISO-639定义的语言码来发布不同的缓存内容(under an otherwise identical key)。
|
refreshpolicyclass |
指定自定义的刷新策略类的全限定类名。这个类继承自com.opensymphony.oscache.web.WebEntryRefreshPolicy
|
refreshpolicyparam |
指定任意需要传给refreshpolicyclass的参数。如果没有指定refreshpolicyclass,则这个值不起作用。
|
相关推荐
在源码层面,OSCache的实现基于软引用和弱引用的概念,使得缓存能够在内存不足时自动释放,防止内存泄露。同时,它支持多级缓存、缓存同步、过期策略等功能,可以根据业务需求进行定制。开发者可以通过配置文件或者...
四、源码分析 osCache的源码提供了深入理解其内部工作原理的机会。通过阅读源码,我们可以了解到如何配置和自定义缓存策略,以及集群中的数据同步是如何实现的。例如,可以查看`CacheManager`类如何初始化和管理...
本文将深入探讨Oscache的核心功能、使用方法以及源码分析,帮助读者全面了解这一工具的精髓。 首先,我们要理解什么是缓存。缓存是一种存储技术,用于临时存储常用数据,以减少对主存储器或网络资源的访问,从而...
博客中可能还会深入到OSCache的源码解析,分析其内部实现机制,包括数据结构的选择、锁的使用以及如何实现高效的缓存替换算法等。通过源码阅读,开发者可以更好地理解OSCache的工作原理,并根据自身需求进行定制化...
总结,OSCache配置URL实现页面缓存的Filter是一种有效的性能提升手段,通过动态配置和源码分析,我们可以深入了解其工作原理并优化Web应用的性能。同时,配合适当的工具,管理和监控缓存变得更为便捷。
开发者可以通过分析这些文件,更深入地理解OSCache 的工作原理和实际应用。 总结来说,OSCache 是一个强大的Java缓存工具,它提供了丰富的功能和高度的灵活性,能够帮助开发者优化应用性能,减少数据库访问压力,...
**源码分析** 对于开发者来说,了解Oscache的源码可以帮助深入理解其工作原理。源码中包含了`CacheManager`、`Cache`、`CacheEntry`等核心类,它们负责缓存的创建、管理和存储。通过对这些类的分析,可以学习到如何...
标题中的"jar 源码(spring+ibatis+..)"指的是这个压缩包包含了与Java开发相关的开源框架Spring、MyBatis以及...通过阅读和分析这些源码,不仅可以提高编程技能,还能培养解决问题的能力,对个人的职业发展大有裨益。
通过分析《Hibernate逍遥游记》的源码,读者不仅可以深入理解Hibernate的工作原理,还能掌握实际项目中如何高效地应用Hibernate进行数据访问层的设计和开发。这些源码实例为学习者提供了一个实战演练的平台,有助于...
SSH(Struts2 + Spring + Hibernate)是一个经典的Java Web开发框架,它整合了三大主流的开源框架,...通过分析和实践这个项目,开发者可以提升自己的技能,了解如何在实际项目中应用这些技术来构建高效、稳定的系统。
通过阅读和分析这个Hibernate3的源代码,开发者可以深入了解其实现机制,比如查询优化、缓存策略、事务管理等,这对于提升Java应用的性能和开发效率具有很大的价值。对于想要深入学习Hibernate或ORM技术的Java开发者...
**描述分析:** 描述中的链接指向了一个CSDN博客文章,虽然具体内容没有给出,但可以推测它提供了与Hibernate缓存相关的源码示例。这意味着我们可能要探讨的是如何在实际项目中应用Hibernate的缓存技术,并通过代码...