寫在前面:上一篇博客寫了spring cache和ehcache的基本介紹,個人建議先把這些最基本的知識了解了才能對今天主題有所感觸。不多說了,開干!
注:引入jar
<!-- 引入ehcache緩存 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.3</version> </dependency>
第一步:首先配置ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true"> <diskStore path="java.io.tmpdir"/> <defaultCache maxEntriesLocalHeap="10000" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <cache name="myCache" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="1000" eternal="false" diskSpoolBufferSizeMB="30" timeToIdleSeconds="300" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LFU" transactionalMode="off"> <persistence strategy="localTempSwap"/> </cache> </ehcache>
第二步:在spring.xml的配置文件中引入schema,
xmlns:aop="http://www.springframework.org/schema/aop"和http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
緩存的配置:
<!-- 啟用緩存注解功能,這個是必須的,否則注解不會生效,另外,該注解一定要聲明在spring主配置文件中才會生效 --> <cache:annotation-driven cache-manager="ehcacheManager"/> <!-- cacheManager工廠類,指定ehcache.xml的位置 --> <bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml" /> </bean> <!-- 聲明cacheManager --> <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcacheManagerFactory" /> </bean>
OK!緩存的相關配置已經完成。下面開始編寫測試程序。這里需要連接數據庫,我就不寫了。這里為了方便就隨便找了之前寫過的model,這個model就是AOP注解實現日志管理的實體,為了偷懶就直接用了,希望你們不要誤解,沒有特殊意義的
第三步:編寫model,這里需要注意,要實現緩存的實體必須要序列化 private static final long serialVersionUID = -6579533328390250520L; 關於序列化的生成這里就不介紹了,大家可以百度看看。

package org.shop.entity; import java.io.Serializable; import java.util.Date; public class SystemLog implements Serializable { private static final long serialVersionUID = -6579533328390250520L; private String id; private String description; private String method; private Long logType; private String requestIp; private String exceptioncode; private String exceptionDetail; private String params; private String createBy; private Date createDate; public String getId() { return id; } public void setId(String id) { this.id = id == null ? null : id.trim(); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description == null ? null : description.trim(); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method == null ? null : method.trim(); } public Long getLogType() { return logType; } public void setLogType(Long logType) { this.logType = logType; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this.requestIp = requestIp == null ? null : requestIp.trim(); } public String getExceptioncode() { return exceptioncode; } public void setExceptioncode(String exceptioncode) { this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim(); } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim(); } public String getParams() { return params; } public void setParams(String params) { this.params = params == null ? null : params.trim(); } public String getCreateBy() { return createBy; } public void setCreateBy(String createBy) { this.createBy = createBy == null ? null : createBy.trim(); } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } }
第四步:編寫dao,service

package org.shop.dao; import org.shop.entity.SystemLog; public interface SystemLogMapper { int deleteByPrimaryKey(String id); int insert(SystemLog record); int insertSelective(SystemLog record); SystemLog selectByPrimaryKey(String id); int updateByPrimaryKeySelective(SystemLog record); int updateByPrimaryKey(SystemLog record); int count(); }

public interface SystemLogService { int deleteSystemLog(String id); int insert(SystemLog record); int insertTest(SystemLog record); SystemLog findSystemLog(String id); int updateSystemLog(SystemLog record); int count(); }
第五步:編寫serviceImpl並添加緩存注解。這里緩存注解的參數不介紹了,不懂得看我上一篇博客,我這里先把需要的注解都寫上了,一會一個一個介紹。
@Service("systemLogService") public class SystemLogServiceImpl implements SystemLogService { @Resource private SystemLogMapper systemLogMapper; @Override public int deleteSystemLog(String id) { return systemLogMapper.deleteByPrimaryKey(id); }
@Override //@CachePut(value="myCache") //@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true) @CacheEvict(value="myCache",key="0",beforeInvocation=true) public int insert(SystemLog record) { return systemLogMapper.insertSelective(record); }
@Override @Cacheable(value="myCache",key="#id") public SystemLog findSystemLog(String id) { return systemLogMapper.selectByPrimaryKey(id); }
@Override public int updateSystemLog(SystemLog record) { return systemLogMapper.updateByPrimaryKeySelective(record); } @Override public int insertTest(SystemLog record) { return systemLogMapper.insert(record); }
@Override @Cacheable(value="myCache",key="0") public int count() { int num = systemLogMapper.count(); return num; } }
第六步:編寫controller,即我們的測試。
@Controller @RequestMapping("systemLogController") public class SystemLogController { @Resource private SystemLogService systemLogService; @RequestMapping("testLog") public ModelAndView testLog(){ ModelMap modelMap = new ModelMap(); SystemLog systemLog = systemLogService.findSystemLog("c30e2398-079a-406b-a2f7-a85fa15ccac7"); modelMap.addAttribute("data", systemLog); return new ModelAndView("index",modelMap); } @RequestMapping("insert") @ResponseBody public boolean Insert(SystemLog record){ systemLogService.insert(record); return true; } @RequestMapping("test1") public ModelAndView test1(){ ModelMap modelMap = new ModelMap(); int num =systemLogService.count(); modelMap.addAttribute("num", num); return new ModelAndView("pageEhcache",modelMap); } }
我們先測試查詢的緩存,即serviceImpl中的 findSystemLog(String id) 方法,我們訪問testLog.do,第一次運行如下圖,注意控制台中的heap和 disk
再一次訪問testLog.do,運行你會發現沒有訪問數據庫,如圖:
到此查詢的緩存我們實現了,但是關於緩存的處理我們並沒有做完,我們應該在深入思考下,在上面查詢的緩存生命周期內,我們對剛才查詢的表進行了增刪改操作,這時我們再訪問該查詢方法,你會發現我們的數據並沒有改變,還是增刪改操作之前的數據(因為緩存的生命還在),這里是不是問題呢?此時我們需要對查詢的緩存進行更新或刪除。
下面我們看serviceImpl中的insert方法和count()方法,count的方法是統計表中的數據總記錄,insert方法是對該表進行新增一條記錄,insert的緩存注解用的是@CacheEvict(value="myCache",key="0",beforeInvocation=true),這里清除的是指定緩存,也就是count方法中@Cacheable(value="myCache",key="0")的,(serviceImpl中注釋的@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true)是清除所有的緩存,這里我就不演示了,道理是一樣的)
這里我提供一個測試pageEhcache.jsp頁面,
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>測試</title> <script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.11.1.min.js"></script> <script type="text/javascript"> function insert(){ var record = $("#formID").serializeArray(); console.info(record); $.ajax({ url : "<%=request.getContextPath()%>/systemLogController/insert.do", type : 'post', async:true, dataType:'json', data : record, success:function(result){ alert("插入成功!"); } }); } </script> </head> <body> <h1><%=new Date()%></h1> <h1>這是一個練習</h1> <form id="formID" action=""> id: <input name="id" type="text"/><br> <input type="button" value="插入" onclick="insert()"/> </form> <br> 總數: <h4>${num}</h4> </body> </html>
我們先訪問test1.do,看下表中的記錄數並注意控制台變化
頁面顯示如下,注意總數是67,
再一次訪問test1.do,沒有訪問數據庫,說明count()方法的緩存生效了,
接下來開始新增記錄,點擊插入按鈕
注意控制台顯示,這里執行了inserSQL語句,並remove了count()方法上的緩存,
接下來再次訪問test1.do,我們看到總數變化了,增加了一條,說明我們把之前count()方法上的緩存刪除了,又執行了查詢總數的sql
再次訪問test1.do,count()方法的緩存生效了,對吧!這個就是@CacheEvict注解的作用。
在insert()方法上還有@CachePut(value="myCache")注解,上面的serviceImpl中注釋了,它的作用是:@CachePut標注的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。
我這里就不做演示了,你們可以自己動手試試。
總結:我個人的理解,對查詢方法增加緩存容易,但對於緩存的更新的處理就比較麻煩,我上面的serviceImpl中寫了三種處理方式,
1.用@CachePut處理,這中方法需要對指定緩存key保持一致,盡管這樣,還是不行,因為它返回的緩存是int(增加或刪除或修改的記錄數或是該記錄的對象,這對我們查詢所有或部分記錄的緩存還是不可行的)
2.用@CacheEvict(value="myCache",key="0",beforeInvocation=true)處理,清除我們指定key的緩存,這種方式缺點是麻煩,需要我們注意每一個緩存的key
3.用@CacheEvict(value="myCache",allEntries=true,beforeInvocation=true)處理,清除所有緩存,這種方式最省事,但會把其他緩存也一同清除。
隨着業務的復雜性的不斷增加,這些處理方式,可能會增加代碼的復雜性,然后我想到的是對DB層進行緩存,可以利用redis,mamchched的進行處理。當然對於一般的web應用運用ehcache已經刻一解決了,但是對大數據量的運用db級別的緩存效果性能可能會更好。
以上純粹是個人想法。另外我也想了想緩存到底在哪些場景下應用會比較好,不知道你們是怎么認為的。也請大家給點建議。