Ehcache是一个快速的、轻量级的、易于使用的、进程内的缓存。它支持read-only和read/write 缓存,内存和磁盘缓存。是一个非常轻量级的缓存实现,而且从1.2之后就支持了集群。
简单地说来,ehcache有以下特点:
快速、简单.多种缓存策略 缓存数据有两级:内存和磁盘,因此无需担心容量问题 缓存数据会在虚拟机重启的过程中写入磁盘可以通过RMI、可插入API 等方式进行分布式缓存具有缓存和缓存管理器的侦听接口支持多缓存管理器实例,以及一个实例的多个缓存区域 提供Hibernate的缓存实现具体ehcache配置和使用可参考我另一篇博客:ehcache的使用 这里重点说明,ehcache在JJfinal中的使用
项目中想使用ehcache缓存,首先需要引入对应jar,Jfinal项目也不例外,但无需在ehcache官网下载,Jfinal文件中就有相应jar包,以“Jfinal-3.0”为了,在Jfinal官网上下载“jfinal-3.0-all.zip”压缩包,并解压:
解压完成后,依次进入目录“jfinal-3.0-all”->“jfinal-3.0-lib”->“ehcache”,其中“jfinal-3.0-lib”文件夹存放着Jfinal需要的全部jar包,也包含ehcache的jar包 “ehcache”文件夹中有以上三个jar包和一个标准的ehcache配置文件;将以上三个jar包都引入项目,并将配置文件“ehcache.xml”放在“src”或“res”目录下
JFinal 已经集成了ehcache缓存,以plugin形式存在,叫“EhCachePlugin”。需要在项目的DemoConfig.java中配置后才能使用:
public class DemoConfig extends JFinalConfig { /** * 配置插件 */ public void configPlugin(Plugins me) { URL url = getClass().getResource("res/ehcache.xml"); me.add(new EhCachePlugin(url)); } }com.jfinal.plugin.ehcache.EhCachePlugin继承了 com.jfinal.plugin.IPlugin 接口;在EhCachePlugin中主要是生成cacheManager,并将cacheManager放在CacheKit 中: 1、在EhCachePlugin中有多个构造函数,都是传递不同类型的ehcache配置文件参数或直接传递CacheManager ,供后面使用(上例中使用了URL形式的参数)
public class EhCachePlugin implements IPlugin { private static CacheManager cacheManager; private String configurationFileName; private URL configurationFileURL; private InputStream inputStream; private Configuration configuration; public EhCachePlugin() { } public EhCachePlugin(CacheManager cacheManager) { EhCachePlugin.cacheManager = cacheManager; } public EhCachePlugin(String configurationFileName) { this.configurationFileName = configurationFileName; } public EhCachePlugin(URL configurationFileURL) { this.configurationFileURL = configurationFileURL; } public EhCachePlugin(InputStream inputStream) { this.inputStream = inputStream; } public EhCachePlugin(Configuration configuration) { this.configuration = configuration; } }2、每个Plugin都会有start方法,在项目启动时执行。 EhCachePlugin 在start方法中调用createCacheManager()方法,创建CacheManager;并将CacheManager放在CacheKit中【CacheKit.init(cacheManager)】,供项目使用
public boolean start() { createCacheManager(); CacheKit.init(cacheManager); return true; } private void createCacheManager() { if (cacheManager != null) return ; if (configurationFileName != null) { cacheManager = CacheManager.create(configurationFileName); return ; } if (configurationFileURL != null) { cacheManager = CacheManager.create(configurationFileURL); return ; } if (inputStream != null) { cacheManager = CacheManager.create(inputStream); return ; } if (configuration != null) { cacheManager = CacheManager.create(configuration); return ; } cacheManager = CacheManager.create(); }com.jfinal.plugin.ehcache.CacheKit是ehcache缓存的操作工具类,在EhCachePlugin中,已经将cacheManager放在CacheKit 中(给CacheKit的cacheManager参数赋值),所以可以在项目中通过CacheKit,来使用ehcache缓存技术
public void index() { int pageNumber = getParaToInt(0, 1); Page<Blog> blogPage = CacheKit.get("blogTest", "blogList"); if(blogPage == null){ blogPage = Blog.me.paginate(pageNumber, 10); CacheKit.put("blogTest", "blogList",blogPage); } setAttr("blogPage",blogPage); render("blog.html"); }CacheKit 中最重要的两个方法是get(String cacheName, Objectkey)与put(String cacheName,Object key, Object value)。get 方法是从cache中取数据,put方法是将数据放入cache。参数cacheName与ehcache.xml中的<cache name="blogTest" …>name属性值对应; 参数key是指取值用到的key;参数 value 是被缓存的数据。对应上例的ehcache.xml配置如下:
<cache name="blogTest" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="1000" eternal="false" diskSpoolBufferSizeMB="30" timeToIdleSeconds="0" timeToLiveSeconds="0" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </cache>ehcache.xml的配置在此不做展开说明,详见我的另一片文章
除了以上介绍的get(String cacheName, Objectkey)方法外,CacheKit还提供了CacheKit.get(String, String, IDataLoader)方法,使用方式如下:
public void index() { Page<Blog> blogPage = CacheKit.get("blogTest", "blogList", new IDataLoader(){ public Object load(){ return Blog.me.paginate(getParaToInt(0, 1), 10); } }); setAttr("blogPage",blogPage); render("blog.html"); }CacheKit.get方法提供了一个IDataLoader接口,该接口中的load()方法在缓存值不存在时才会被调用。该方法的具体操作流程是:首先以 cacheName=blogTest 以及 key=blogList 为参数去缓存取数据,如果缓存中数据存在就直接返回该数据,不存在则调用 IDataLoader.load()方法来获取数据,并将获取到的数据通过put(cacheName, key, data)放在cache中。
CacheKit中的其他方法:
List getKeys(String cacheName):获取名称为“cacheName”的cache中全部key。remove(String cacheName, Object key):删除某一缓存removeAll(String cacheName):删除cache中的全部缓存static Cache getOrAddCache(String cacheName):根据cacheName获取或创建cache。 getOrAddCache方法是put、get方法的基础。 如果ehcache.xml中未配置以cacheName命名的cache,getOrAddCache方法会调用 cacheManager.addCacheIfAbsent(cacheName)创建cache,缓存策略使用 default除了工具类外,还可以通过配置拦截器的方式使用ehcache缓存。 CacheInterceptor可以将 action 所需数据全部缓存起来,下次请求到来时如果cache存在则直接使用数据并render,而不会去调用action。此用法可使 action 完全不受 cache 相关代码所污染,即插即用,以下是示例代码:
@Before(CacheInterceptor.class) public void index() { int pageNumber = getParaToInt(0, 1); Page<Blog> blogPage = Blog.me.paginate(pageNumber, 10); setAttr("blogPage", blogPage); render("blog.html"); }运行结果如下,可以看出第二次执行,没有打印sql,意味着没有查询数据库,直接从缓存中取值:
上例中的用法将使用 actionKey作为cacheName(使用具体的url(actionKey+参数)作为cacheKey),在使用之前需要在 ehcache.xml中配置以actionKey命名的cache 如:<cache name="/blog" …>,注意 actionKey 作为cacheName配置时斜杠”/”不能省略(如果没有在ehcache.xml配置对应的cache,系统会使用default,详见CacheKit的getOrAddCache方法)。此外CacheInterceptor 还可以与CacheName注解配合使用,以此来取代默认的 actionKey 作为 cacheName,以下是示例代码:
@Before(CacheInterceptor.class) @CacheName("bloglist") public void index() { int pageNumber = getParaToInt(0, 1); Page<Blog> blogPage = Blog.me.paginate(pageNumber, 10); setAttr("blogPage", blogPage); render("blog.html"); }以上用法需要在ehcache.xml中配置名为bloglist的cache如:<cache name="bloglist" …>
EvictInterceptor 可以根据 CacheName 注解自动清除缓存。以下是示例代码:
@Before(EvictInterceptor.class) @CacheName("bloglist") public void delete() { Blog.me.deleteById(getParaToInt()); redirect("/blog"); }上例中的用法将清除 cacheName 为 blogList 的缓存数据,与其配合的CacheInterceptor 会自动更新 cacheName 为 blogList 的缓存数据。
查看以下EvictInterceptor源代码可以看到,在执行完inv.invoke()后,调用CacheKit.removeAll(buildCacheName(inv))方法,将名为cacheName的缓存全部清除。
public class EvictInterceptor implements Interceptor{ final public void intercept(Invocation inv) { inv.invoke(); CacheKit.removeAll(buildCacheName(inv)); } }参考:《Jfinal用户手册》 Jfinal官网:http://www.jfinal.com/