在項目中使用緩存我OSCache,今天有時間將所有的EHCache的緩存的應用關注一下。首先我們看看Spring和EHCache採用AOP實現的緩存實現。
1.首先使用EHCache編寫EHCache的配置文件。
- <ehcache>
- <diskStore path="java.io.tmpdir" />
- <defaultCache maxElementsInMemory="10000" eternal="false"
- timeToIdleSeconds="2" timeToLiveSeconds="5" overflowToDisk="true"
- maxElementsOnDisk="10000000" diskPersistent="false"
- diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
- <cache name="testCache" maxElementsInMemory="10000"
- maxElementsOnDisk="1000" eternal="false" overflowToDisk="false"
- diskSpoolBufferSizeMB="20" timeToIdleSeconds="60" timeToLiveSeconds="3600"
- memoryStoreEvictionPolicy="LFU" />
- </ehcache>
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="2" timeToLiveSeconds="5" overflowToDisk="true"
maxElementsOnDisk="10000000" diskPersistent="false"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
<cache name="testCache" maxElementsInMemory="10000"
maxElementsOnDisk="1000" eternal="false" overflowToDisk="false"
diskSpoolBufferSizeMB="20" timeToIdleSeconds="60" timeToLiveSeconds="3600"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
2.編寫AOP的方法攔截器,此處採用環繞通知的方式實現方法攔截。
- package com.easyway.ecache.service;
- import java.io.Serializable;
- import net.sf.ehcache.Cache;
- import net.sf.ehcache.Element;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.beans.factory.InitializingBean;
- /**
- * AOP方法攔截實現緩存的更新的
- *
- * MethodInterceptor:在方法調用會自動調用
- * InitializingBean:系統初始化時會自動初始化此類的特定的方法afterPropertiesSet()
- * @author longgangbai
- *
- */
- public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean{
- private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
- private Cache cache;
- public void setCache(Cache cache) {
- this.cache = cache;
- }
- //invoke方法會在spring配置文件裏的,指明的cache攔截的方法的調用時,自動觸發它,如這個項目裏,
- //當運行HelloEhcacheSpring.java類時,在showPersonsInfo方法裏調用到personManager.getList()方法時,
- //它就會先調到這裏來執行,執行完才行下執行它的業務
- public Object invoke(MethodInvocation invocation) throws Throwable {
- //這個表示哪個類調用(或觸發)了這個MethodCacheInterceptor,
- String targetName = invocation.getThis().getClass().getName();
- //這個表示哪個方法觸發了這個類(MethodCacheInterceptor)方法(invoke)的調用,
- String methodName = invocation.getMethod().getName();
- //調用的參數,這裏沒有參數
- Object[] arguments = invocation.getArguments();
- Object result;
- //這裏得出的是:manager.PersonManagerImpl.getList
- String cacheKey = getCacheKey(targetName, methodName, arguments);
- Element element = cache.get(cacheKey);
- if (element == null) {
- // call target/sub-interceptor
- //這個就是調用數據訪問方法,
- result = invocation.proceed();
- //如這裏調用了getList()方法,會先打印出"get Person from DB" ,
- //然後將結果集放入到result裏面去,這裏由於使用的是自己配置只能放入10個元素的ehcache,
- //所以這裏的result是ArrayList<E> ,它裏面存放的是elementData[10],並將getList得到的結果放入到elementData裏面去了
- System.out.println("set into cache");
- // cache method result
- //下面方法執行後,將cacheKey與數據集連起來,cacheKey是用來標識這個element的標誌,我們可以有多個element(各自是來自不同的數據訪問方法而形成的),區分它們就是用cacheKey,
- //這裏的新生成後的element,含有cacheKey,還在element創建時間,訪問時間,還有命令次數等cache的屬性,我覺得它就像是一個小cache一樣,下次要不要更新它就要看它的這些屬性來決定。
- element = new Element(cacheKey, (Serializable) result);
- //放入cache中
- cache.put(element);
- }else{
- logger.debug("come from cache ...!");
- }
- //完成cache操作
- System.out.println("out cache");
- return element.getValue();
- }
- /**
- * 緩存特定的類:
- * @param targetName
- * @param methodName
- * @param arguments
- * @return
- */
- private String getCacheKey(String targetName, String methodName,
- Object[] arguments) {
- StringBuffer sb = new StringBuffer();
- sb.append(targetName).append(".").append(methodName);
- if ((arguments != null) && (arguments.length != 0)) {
- for (int i = 0; i < arguments.length; i++) {
- sb.append(".").append(arguments[i]);
- }
- }
- return sb.toString();
- }
- /**
- * 初始化時調用
- */
- public void afterPropertiesSet() throws Exception {
- if(null == cache) {
- throw new IllegalArgumentException("Cache should not be null.");
- }
- }
- }
package com.easyway.ecache.service;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* AOP方法攔截實現緩存的更新的
*
* MethodInterceptor:在方法調用會自動調用
* InitializingBean:系統初始化時會自動初始化此類的特定的方法afterPropertiesSet()
* @author longgangbai
*
*/
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean{
private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
//invoke方法會在spring配置文件裏的,指明的cache攔截的方法的調用時,自動觸發它,如這個項目裏,
//當運行HelloEhcacheSpring.java類時,在showPersonsInfo方法裏調用到personManager.getList()方法時,
//它就會先調到這裏來執行,執行完才行下執行它的業務
public Object invoke(MethodInvocation invocation) throws Throwable {
//這個表示哪個類調用(或觸發)了這個MethodCacheInterceptor,
String targetName = invocation.getThis().getClass().getName();
//這個表示哪個方法觸發了這個類(MethodCacheInterceptor)方法(invoke)的調用,
String methodName = invocation.getMethod().getName();
//調用的參數,這裏沒有參數
Object[] arguments = invocation.getArguments();
Object result;
//這裏得出的是:manager.PersonManagerImpl.getList
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null) {
// call target/sub-interceptor
//這個就是調用數據訪問方法,
result = invocation.proceed();
//如這裏調用了getList()方法,會先打印出"get Person from DB" ,
//然後將結果集放入到result裏面去,這裏由於使用的是自己配置只能放入10個元素的ehcache,
//所以這裏的result是ArrayList<E> ,它裏面存放的是elementData[10],並將getList得到的結果放入到elementData裏面去了
System.out.println("set into cache");
// cache method result
//下面方法執行後,將cacheKey與數據集連起來,cacheKey是用來標識這個element的標誌,我們可以有多個element(各自是來自不同的數據訪問方法而形成的),區分它們就是用cacheKey,
//這裏的新生成後的element,含有cacheKey,還在element創建時間,訪問時間,還有命令次數等cache的屬性,我覺得它就像是一個小cache一樣,下次要不要更新它就要看它的這些屬性來決定。
element = new Element(cacheKey, (Serializable) result);
//放入cache中
cache.put(element);
}else{
logger.debug("come from cache ...!");
}
//完成cache操作
System.out.println("out cache");
return element.getValue();
}
/**
* 緩存特定的類:
* @param targetName
* @param methodName
* @param arguments
* @return
*/
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
/**
* 初始化時調用
*/
public void afterPropertiesSet() throws Exception {
if(null == cache) {
throw new IllegalArgumentException("Cache should not be null.");
}
}
}
3.Spring的關於緩存的配置類似事物的配置:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
- default-lazy-init="true">
- <!--配置緩存管理器 -->
- <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- <property name="configLocation">
- <value>ehcache.xml</value>
- </property>
- </bean>
- <!-- 創建緩存的工廠的應用 -->
- <bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
- <property name="cacheManager">
- <ref local="cacheManager"/>
- </property>
- <property name="cacheName">
- <value>com.easyway.MethodCache</value>
- </property>
- </bean>
- <!-- 自定義緩存攔截器 -->
- <bean id="methodCacheInterceptor" class="com.easyway.ecache.service.MethodCacheInterceptor">
- <property name="cache">
- <ref local="methodCache"/>
- </property>
- </bean>
- <!-- 自定義攔截器 -->
- <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice">
- <ref local="methodCacheInterceptor"/>
- </property>
- <!-- 下面的配置就使得在數據訪問時,cache將攔截從數據庫獲取的數據,與cache數據比較,如有就不放入cache,沒有就放入,更新到數據庫去,也是先存入cache,再更新到數據庫中去 -->
- <property name="patterns">
- <list>
- <value>.*getServiceName</value>
- <value>.*testMethod</value>
- </list>
- </property>
- </bean>
- <!-- 聲明一個服務 -->
- <bean id = "ticketServiceTarget" class="com.easyway.ecache.service.TicketService" />
- <!-- 相關的服務 -->
- <bean id="ticketService"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="target">
- <ref local="ticketServiceTarget"/>
- </property>
- <property name="interceptorNames">
- <list>
- <value>methodCachePointCut</value>
- </list>
- </property>
- </bean>
- </beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-lazy-init="true">
<!--配置緩存管理器 -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>ehcache.xml</value>
</property>
</bean>
<!-- 創建緩存的工廠的應用 -->
<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>com.easyway.MethodCache</value>
</property>
</bean>
<!-- 自定義緩存攔截器 -->
<bean id="methodCacheInterceptor" class="com.easyway.ecache.service.MethodCacheInterceptor">
<property name="cache">
<ref local="methodCache"/>
</property>
</bean>
<!-- 自定義攔截器 -->
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor"/>
</property>
<!-- 下面的配置就使得在數據訪問時,cache將攔截從數據庫獲取的數據,與cache數據比較,如有就不放入cache,沒有就放入,更新到數據庫去,也是先存入cache,再更新到數據庫中去 -->
<property name="patterns">
<list>
<value>.*getServiceName</value>
<value>.*testMethod</value>
</list>
</property>
</bean>
<!-- 聲明一個服務 -->
<bean id = "ticketServiceTarget" class="com.easyway.ecache.service.TicketService" />
<!-- 相關的服務 -->
<bean id="ticketService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="ticketServiceTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
</list>
</property>
</bean>
</beans>
4.測試服務類:
- package com.easyway.ecache.service;
- import java.util.List;
- /**
- * 對其所有的以testMethod* ,getServiceName方式命令的方法,
- * 進行緩存處理。當調用其他命令時,不進行緩存
- * @author longgangbai
- *
- */
- @SuppressWarnings("unchecked")
- public class TicketService {
- public String testMethod(){
- System.out.println("沒走緩存,直接調用TestService.testMethod()");
- return "china";
- }
- public void updateMethod(){
- System.out.println("updateMethod");
- }
- public void insertMethod(){
- System.out.println("insertMethod");
- }
- public void deleteMethod(){
- System.out.println("deleteMethod");
- }
- /**
- * 需要緩存的集合
- */
- private List ticketList;
- /**
- * 需要緩存的服務名稱
- */
- private String serviceName;
- public String getServiceName() {
- return serviceName;
- }
- public void setServiceName(String serviceName) {
- this.serviceName = serviceName;
- }
- public List getTicketList() {
- return ticketList;
- }
- public void setTicketList(List ticketList) {
- this.ticketList = ticketList;
- }
- /**
- * 修改的服務端名稱備註但是不緩存
- * @param serviceName
- */
- public void changesetServiceName(String serviceName) {
- this.serviceName = serviceName;
- }
- }
package com.easyway.ecache.service;
import java.util.List;
/**
* 對其所有的以testMethod* ,getServiceName方式命令的方法,
* 進行緩存處理。當調用其他命令時,不進行緩存
* @author longgangbai
*
*/
@SuppressWarnings("unchecked")
public class TicketService {
public String testMethod(){
System.out.println("沒走緩存,直接調用TestService.testMethod()");
return "china";
}
public void updateMethod(){
System.out.println("updateMethod");
}
public void insertMethod(){
System.out.println("insertMethod");
}
public void deleteMethod(){
System.out.println("deleteMethod");
}
/**
* 需要緩存的集合
*/
private List ticketList;
/**
* 需要緩存的服務名稱
*/
private String serviceName;
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public List getTicketList() {
return ticketList;
}
public void setTicketList(List ticketList) {
this.ticketList = ticketList;
}
/**
* 修改的服務端名稱備註但是不緩存
* @param serviceName
*/
public void changesetServiceName(String serviceName) {
this.serviceName = serviceName;
}
}
5.測試用例
- package com.easyway.ecache.service;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * 這裏使用了ehcache與spring結合,這裏並沒用用到數據庫,用spring只是用來管理bean,
- * 這裏用ehcache就相當於數據庫,存放對象信息
- * @author longgangbai
- */
- @SuppressWarnings({"unchecked"})
- public class HelloEhcacheSpring{
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
- TicketService ticketSrv = (TicketService) context.getBean("ticketService");
- //配置了spring就可以從配置文件裏找到對應的接口實現類,再生成實例對象,以完成業務處理
- String srvName0=ticketSrv.testMethod();
- //獲取初始化服務端名稱
- System.out.println("srvName0="+srvName0);
- //設置存儲的名稱
- ticketSrv.setServiceName("ticketService");
- String srvName1=ticketSrv.testMethod();
- //獲取服務端名稱
- System.out.println("srvName1="+srvName1);
- //修改服務名稱但是不緩存
- ticketSrv.updateMethod();
- String srvName2=ticketSrv.testMethod();
- //獲取服務端名稱來源自緩存注意觀察
- System.out.println("srvName2="+srvName2);
- }
- }
package com.easyway.ecache.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 這裏使用了ehcache與spring結合,這裏並沒用用到數據庫,用spring只是用來管理bean,
* 這裏用ehcache就相當於數據庫,存放對象信息
* @author longgangbai
*/
@SuppressWarnings({"unchecked"})
public class HelloEhcacheSpring{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
TicketService ticketSrv = (TicketService) context.getBean("ticketService");
//配置了spring就可以從配置文件裏找到對應的接口實現類,再生成實例對象,以完成業務處理
String srvName0=ticketSrv.testMethod();
//獲取初始化服務端名稱
System.out.println("srvName0="+srvName0);
//設置存儲的名稱
ticketSrv.setServiceName("ticketService");
String srvName1=ticketSrv.testMethod();
//獲取服務端名稱
System.out.println("srvName1="+srvName1);
//修改服務名稱但是不緩存
ticketSrv.updateMethod();
String srvName2=ticketSrv.testMethod();
//獲取服務端名稱來源自緩存注意觀察
System.out.println("srvName2="+srvName2);
}
}
6.測試結果:
沒走緩存,直接調用TestService.testMethod()
打印信息如下:
set into cache
out cache
srvName0=china
out cache
srvName1=china
updateMethod
out cache
srvName2=china