在实际应用中除了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为了避免这个麻烦,采用的就是当数据一旦发生改变的时候就清除全部的查询缓存,而不是只去清除相关的缓存,其实这里可以采用一种订阅式的模型,呵呵,当然,也增加了框架的复杂度。
数据缓存使用的应该是最多的,效果也是很明显的。
以上三种缓存是目前缓存实现时通常碰到的三种状况,里面按使用的多少来排序应该是:数据缓存、页面缓存和处理缓存;实现的难度上从难到易的顺序应该是:处理缓存、页面缓存、数据缓存;对于系统响应速度提升的效果来说从最好到好的顺序应该是:页面缓存、处理缓存、数据缓存。