在實際應用中除了JSP標簽庫,還可以使用OSCache提供的Java API.下面我來介紹一個實用的Java類,使用GeneralCacheAdministrator來建立,刷新和管理緩存.
GeneralCacheAdministrator類常用的方法有:
public Object getFromCache(String key) throws NeedsRefreshException; //從緩存中獲取一個key標識的對象.
public Object getFromCache(String key, int refreshPeriod) throws NeedsRefreshException; //從緩存中獲取一個key標識的對象. refreshPeriod刷新周期,標識此對象在緩存中保存的時間(單位:秒)
public void putInCache(String key, Object content); //存儲一個由Key標識的緩存對象.
public void putInCache(String key, Object content, String[] groups); //存儲一個由Key標識的屬於groups中所有成員的緩存對象.
public void flushEntry(String key); //更新一個Key標識的緩存對象.
public void flushGroup(String group); //更新一組屬於groupr標識的所有緩存對象.
public void flushAll(); //更新所有緩存.
public void cancelUpdate(String key); //取消更新,只用於在處理捕獲的NeedsRefreshException異常並嘗試生成新緩存內容失效的時候.
public void removeEntry(String key); //從緩存中移除一個key標識的對象
案例:
1、對象Bean
package com.ljq.test; import java.text.SimpleDateFormat; import java.util.Date; /** * 對象Bean * * @author 林計欽 * @version 1.0 Aug 16, 2013 7:50:06 PM */ public class User { private int id; private String name; private String sex; private int age; private Date accessTime; public User(int id) { super(); this.id = id; this.accessTime = new Date(); } public String toString() { String date=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(accessTime); return "User info is : id=" + id + " accessTime=" + date; } public User(String name, String sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } public User() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Date getAccessTime() { return accessTime; } public void setAccessTime(Date accessTime) { this.accessTime = accessTime; } }
2、緩存操作類
package com.ljq.test; import com.opensymphony.oscache.base.NeedsRefreshException; import com.opensymphony.oscache.general.GeneralCacheAdministrator; /** * 緩存操作類 * * @author 林計欽 * @version 1.0 Aug 16, 2013 7:48:07 PM */ public class BaseCache extends GeneralCacheAdministrator { private static final long serialVersionUID = 6239736145696260016L; private int refreshPeriod; // 過期時間(單位為秒) private String keyPrefix; // 關鍵字前綴字符 public BaseCache(String keyPrefix, int refreshPeriod) { super(); this.keyPrefix = keyPrefix; this.refreshPeriod = refreshPeriod; } /** * 添加被緩存的對象 * * @param key * @param value */ public void put(String key, Object value) { this.putInCache(this.keyPrefix + "_" + key, value); } /** * 刪除被緩存的對象 * * @param key */ public void remove(String key){ this.removeEntry(this.keyPrefix + "_" + key); } /** * 刪除所有被緩存的對象 */ public void removeAll(){ this.flushAll(); } /** * 獲取被緩存的對象 * * @param key * @return * @throws Exception */ public Object get(String key) throws NeedsRefreshException { return this.getFromCache(this.keyPrefix + "_" + key, this.refreshPeriod); } public void cancel(String key){ this.cancelUpdate(key); } public void put(String key, Object value, String[] groups) { this.putInCache(this.keyPrefix + "_" + key, value, groups); } /** * 刪除該組的緩存對象 * * @param group */ public void removeObjectByGroup(String group) { this.flushGroup(group); } }
3、Cache管理類
package com.ljq.test; /** * Cache管理類 * * @author 林計欽 * @version 1.0 Aug 16, 2013 7:49:35 PM */ public class CacheManager { private boolean update=false; private BaseCache userCache; private static CacheManager instance; private static Object lock = new Object(); private CacheManager() { // 這個根據配置文件來,初始BaseCache而已; userCache = new BaseCache("user", 120); } public static CacheManager getInstance() { if (instance == null) { synchronized (lock) { if (instance == null) { instance = new CacheManager(); } } } return instance; } public void putUser(User user) { userCache.put(user.getId() + "", user); } public void removeUser(String id) { userCache.remove(id); } public void removeAllUser() { userCache.removeAll(); } public User getUser(int id) { try { //從Cache中獲得 return (User) userCache.get(id + ""); } catch (Exception e) { //Cache中沒有則從DB庫獲取 System.out.println(String.format("[%s]從db中獲取", id+"")); User user = new User(id); //把獲取的對象再次存入Cache中 this.putUser(user); update=true; return user; }finally{ if(!update){ //如果Cache中的內容更新出現異常,則終止該方法 userCache.cancel(id + ""); //取消對id的更新 } } } }
4、測試類
package com.ljq.test; /** * 測試類 * * @author 林計欽 * @version 1.0 Aug 16, 2013 7:51:19 PM */ public class UserCacheTest { public static void main(String[] args) { CacheManager cm = CacheManager.getInstance(); UserCacheTest test = new UserCacheTest(); test.print(cm); } public void print(CacheManager cm) { User user = null; for (int i = 0; i < 20; i++) { user = cm.getUser(100); System.out.println("<<" + i + ">>: " + user); if (i == 5) { // 刪除緩存id的對象 cm.removeUser(100 + ""); } if (i == 10) { // 刪除所有緩存的對象 cm.removeAllUser(); } // 睡眠部分 try { Thread.sleep(1000); } catch (Exception e) { } } } }
5、執行后控制台打印的結果
[100]從db中獲取 <<0>>: User info is : id=100 accessTime=2013-08-16 21:20:27 <<1>>: User info is : id=100 accessTime=2013-08-16 21:20:27 <<2>>: User info is : id=100 accessTime=2013-08-16 21:20:27 <<3>>: User info is : id=100 accessTime=2013-08-16 21:20:27 <<4>>: User info is : id=100 accessTime=2013-08-16 21:20:27 <<5>>: User info is : id=100 accessTime=2013-08-16 21:20:27 [100]從db中獲取 <<6>>: User info is : id=100 accessTime=2013-08-16 21:20:33 <<7>>: User info is : id=100 accessTime=2013-08-16 21:20:33 <<8>>: User info is : id=100 accessTime=2013-08-16 21:20:33 <<9>>: User info is : id=100 accessTime=2013-08-16 21:20:33 <<10>>: User info is : id=100 accessTime=2013-08-16 21:20:33 [100]從db中獲取 <<11>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<12>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<13>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<14>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<15>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<16>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<17>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<18>>: User info is : id=100 accessTime=2013-08-16 21:20:38 <<19>>: User info is : id=100 accessTime=2013-08-16 21:20:38
小結及其引申
緩存是在提升系統響應時常用的一種技術,在系統緩存上通常采用的是有頁面緩存、處理緩存和數據緩存這三種具體的類別,應該說這三種緩存在實現上還是稍有不同,盡管底層的緩存實現是一樣的。
頁面緩存
頁面緩存是指對頁面中的內容片斷進行緩存的方案。比如頁面中有一個部分是顯示欄目中的內容的,那么就可以緩存這個部分,在進行第二次請求的時候就直接從緩存中取出這部分的內容(其實就是這部分的html了),這種情況下,緩存的作用其實非常明顯,在典型的action+service+dao這樣的結構中,在采用頁面緩存后就意味着不需要經過action、service、dao這些層次的處理了,而是直接就返回了,對於系統響應速度的提升來說是非常明顯的。
頁面緩存通常采用oscache來進行實現,oscache提供了一個jsp tag,可通過這個tag來包含需要緩存的內容部分,當然,緩存的這個內容部分需要有對服務器的請求或邏輯計算等的,可想而知,去緩存一段靜態html是沒有意義的。
其次需要定義緩存的這段內容的key,例如我們要去緩存頁面中某個欄目的某頁的內容,對於這段內容而言唯一的key就是欄目ID以及當前頁數,這樣就組成了這段緩存的key了,其實這個部分看起來好像是很簡單,但有些時候會很麻煩,要仔細的想清楚這段內容的唯一的標識的key到底是什么,^_^,通常的做法其實可以從action中需要獲取的參數或service接口的參數來決定....
頁面緩存中還需要做的一個步驟就是通知緩存需要更新,頁面緩存和其他緩存稍有不同,需要告訴它,這個時候不能再使用緩存中的內容了,需要從后台再重新獲取來生成新的緩存內容,這個其實很簡單,因為很難在后台發生變化的時候自己來更新緩存的內容,只能是去通知它,然后讓它再次發起請求來生成新的內容放入緩存中。
頁面的緩存的使用對於系統的響應速度確實會有很大的提升,在實現頁面緩存時最麻煩的主要是緩存的key的定義以及緩存更新的通知,緩存key的定義這個自然框架是沒法解決的,不過緩存更新的通知其實在框架中可以考慮一種通知模型的,^_^,就像事件通知那樣........在實際的項目中,可以自己去實現一個這樣的通知模型或者就是簡單的采用單例方式來標識某個key是否需要更新。
頁面緩存在實際的項目中使用非常的多。
處理緩存
處理緩存是指對於action、service、dao或者系統層次中的某方法進行緩存,說直接點,就是對某個類的某個方法的結果做緩存,這樣在下次進行完全相同的請求的時候就可以直接取緩存了,這種響應速度的提升也是非常明顯的。
處理緩存在現在的情況下其實采用任務的緩存工具包都可以實現,如oscache、ehcache、jbosscache等,但目前還沒有處理緩存框架的出現,這個和處理緩存是否應該存在的意義也是有關系的,處理緩存框架要做到的其實就像攔截一樣的方式,和oscache tag類似。
同樣,處理緩存的麻煩也在於怎么樣去定義這個key,很多情況下可以根據方法的輸入作為key,方法的輸出作為key的值,但也會有其他一些復雜的情況,這個時候定義key就會變得復雜些了。
處理緩存同樣有通知更新緩存的情況,和頁面緩存基本是一樣的。
應該說,處理緩存和頁面緩存非常的相似,從實現上來說基本是完全一致的,在使用上來講處理緩存使用的好像不多。
數據緩存
數據緩存估計大家都很熟悉,就是對系統的數據進行緩存的方式,典型的就是Hibernate的一級、二級數據緩存。
數據緩存在實現上如果是用hibernate的話更多的是直接使用hibernate的一級、二級以及查詢緩存,如果自己要實現的話可以去參考hibernate的實現機制。
數據緩存的key在一級、二級緩存中采用的都是數據的標識鍵的值的方式,查詢緩存采用的是查詢參數、查詢語句的方式。
數據緩存的更新則是hibernate在進行存儲時直接更新緩存的內容,而對於查詢緩存則是采用全部直接清除的方式,這樣在下次進行查詢時自然會重新去查詢,^_^,大家可能會想,為什么頁面緩存和處理緩存不采用這樣的方式來實現緩存的更新,稍微想想就知道了,在后台發生改變的時候其實是不知道需要移除哪些key的,所以hibernate為了避免這個麻煩,采用的就是當數據一旦發生改變的時候就清除全部的查詢緩存,而不是只去清除相關的緩存,其實這里可以采用一種訂閱式的模型,呵呵,當然,也增加了框架的復雜度。
數據緩存使用的應該是最多的,效果也是很明顯的。
以上三種緩存是目前緩存實現時通常碰到的三種狀況,里面按使用的多少來排序應該是:數據緩存、頁面緩存和處理緩存;實現的難度上從難到易的順序應該是:處理緩存、頁面緩存、數據緩存;對於系統響應速度提升的效果來說從最好到好的順序應該是:頁面緩存、處理緩存、數據緩存。
