Spring中AOP實現EHCache的整合(一)

在項目中使用緩存我OSCache,今天有時間將所有的EHCache的緩存的應用關注一下。首先我們看看Spring和EHCache採用AOP實現的緩存實現。

 

1.首先使用EHCache編寫EHCache的配置文件。

Java代碼 複製代碼 收藏代碼
  1. <ehcache>   
  2.      <diskStore path="java.io.tmpdir" />   
  3.      <defaultCache maxElementsInMemory="10000" eternal="false"  
  4.          timeToIdleSeconds="2" timeToLiveSeconds="5" overflowToDisk="true"  
  5.          maxElementsOnDisk="10000000" diskPersistent="false"  
  6.          diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />   
  7.      <cache name="testCache" maxElementsInMemory="10000"  
  8.          maxElementsOnDisk="1000" eternal="false" overflowToDisk="false"  
  9.          diskSpoolBufferSizeMB="20" timeToIdleSeconds="60" timeToLiveSeconds="3600"  
  10.         memoryStoreEvictionPolicy="LFU" />   
  11. </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的方法攔截器,此處採用環繞通知的方式實現方法攔截。

Java代碼 複製代碼 收藏代碼
  1. package com.easyway.ecache.service;   
  2. import java.io.Serializable;   
  3.   
  4. import net.sf.ehcache.Cache;   
  5. import net.sf.ehcache.Element;   
  6.   
  7. import org.aopalliance.intercept.MethodInterceptor;   
  8. import org.aopalliance.intercept.MethodInvocation;   
  9. import org.apache.commons.logging.Log;   
  10. import org.apache.commons.logging.LogFactory;   
  11. import org.springframework.beans.factory.InitializingBean;   
  12. /**  
  13.  * AOP方法攔截實現緩存的更新的  
  14.  *   
  15.  * MethodInterceptor:在方法調用會自動調用  
  16.  * InitializingBean:系統初始化時會自動初始化此類的特定的方法afterPropertiesSet()  
  17.  * @author longgangbai  
  18.  *  
  19.  */  
  20. public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean{    
  21.     private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);      
  22.          
  23.   
  24.         private Cache cache;      
  25.          
  26.         public void setCache(Cache cache) {      
  27.             this.cache = cache;      
  28.         }      
  29.               
  30.         //invoke方法會在spring配置文件裏的,指明的cache攔截的方法的調用時,自動觸發它,如這個項目裏,   
  31.         //當運行HelloEhcacheSpring.java類時,在showPersonsInfo方法裏調用到personManager.getList()方法時,   
  32.         //它就會先調到這裏來執行,執行完才行下執行它的業務      
  33.         public Object invoke(MethodInvocation invocation) throws Throwable {      
  34.             //這個表示哪個類調用(或觸發)了這個MethodCacheInterceptor,   
  35.             String targetName = invocation.getThis().getClass().getName();   
  36.             //這個表示哪個方法觸發了這個類(MethodCacheInterceptor)方法(invoke)的調用,   
  37.             String methodName = invocation.getMethod().getName();   
  38.             //調用的參數,這裏沒有參數      
  39.             Object[] arguments = invocation.getArguments();   
  40.             Object result;      
  41.          
  42.             //這裏得出的是:manager.PersonManagerImpl.getList      
  43.             String cacheKey = getCacheKey(targetName, methodName, arguments);   
  44.             Element element = cache.get(cacheKey);      
  45.             if (element == null) {      
  46.                 // call target/sub-interceptor      
  47.                 //這個就是調用數據訪問方法,   
  48.                 result = invocation.proceed();   
  49.                 //如這裏調用了getList()方法,會先打印出"get Person from DB" ,   
  50.                 //然後將結果集放入到result裏面去,這裏由於使用的是自己配置只能放入10個元素的ehcache,   
  51.                 //所以這裏的result是ArrayList<E> ,它裏面存放的是elementData[10],並將getList得到的結果放入到elementData裏面去了      
  52.                 System.out.println("set into cache");      
  53.                 // cache method result      
  54.                 //下面方法執行後,將cacheKey與數據集連起來,cacheKey是用來標識這個element的標誌,我們可以有多個element(各自是來自不同的數據訪問方法而形成的),區分它們就是用cacheKey,      
  55.                 //這裏的新生成後的element,含有cacheKey,還在element創建時間,訪問時間,還有命令次數等cache的屬性,我覺得它就像是一個小cache一樣,下次要不要更新它就要看它的這些屬性來決定。      
  56.                 element = new Element(cacheKey, (Serializable) result);   
  57.                 //放入cache中      
  58.                 cache.put(element);   
  59.             }else{   
  60.                 logger.debug("come from cache ...!");   
  61.             }      
  62.             //完成cache操作      
  63.             System.out.println("out cache");   
  64.             return element.getValue();      
  65.         }      
  66.          
  67.         /**  
  68.          * 緩存特定的類:  
  69.          * @param targetName  
  70.          * @param methodName  
  71.          * @param arguments  
  72.          * @return  
  73.          */  
  74.         private String getCacheKey(String targetName, String methodName,      
  75.                 Object[] arguments) {      
  76.             StringBuffer sb = new StringBuffer();      
  77.             sb.append(targetName).append(".").append(methodName);      
  78.             if ((arguments != null) && (arguments.length != 0)) {      
  79.                 for (int i = 0; i < arguments.length; i++) {      
  80.                     sb.append(".").append(arguments[i]);      
  81.                 }      
  82.             }      
  83.             return sb.toString();      
  84.         }      
  85.          
  86.         /**  
  87.          * 初始化時調用  
  88.          */  
  89.         public void afterPropertiesSet() throws Exception {      
  90.             if(null == cache) {      
  91.                 throw new IllegalArgumentException("Cache should not be null.");      
  92.             }      
  93.         }      
  94.          
  95.     }    
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的關於緩存的配置類似事物的配置:

Java代碼 複製代碼 收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>     
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
  3.        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"     
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd      
  5.             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd      
  6.             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"      
  7.        default-lazy-init="true">     
  8.        
  9.     <!--配置緩存管理器 -->     
  10.     <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">     
  11.         <property name="configLocation">        
  12.             <value>ehcache.xml</value>        
  13.         </property>       
  14.     </bean>     
  15.        
  16.     <!-- 創建緩存的工廠的應用 -->   
  17.     <bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">     
  18.         <property name="cacheManager">     
  19.             <ref local="cacheManager"/>     
  20.         </property>     
  21.         <property name="cacheName">     
  22.             <value>com.easyway.MethodCache</value>     
  23.         </property>     
  24.     </bean>     
  25.        
  26.     <!-- 自定義緩存攔截器 -->   
  27.     <bean id="methodCacheInterceptor" class="com.easyway.ecache.service.MethodCacheInterceptor">     
  28.         <property name="cache">     
  29.             <ref local="methodCache"/>     
  30.         </property>     
  31.     </bean>     
  32.        
  33.     <!-- 自定義攔截器 -->   
  34.     <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">     
  35.         <property name="advice">     
  36.             <ref local="methodCacheInterceptor"/>     
  37.         </property>     
  38.         <!-- 下面的配置就使得在數據訪問時,cache將攔截從數據庫獲取的數據,與cache數據比較,如有就不放入cache,沒有就放入,更新到數據庫去,也是先存入cache,再更新到數據庫中去 -->     
  39.         <property name="patterns">     
  40.             <list>     
  41.                 <value>.*getServiceName</value>    
  42.                 <value>.*testMethod</value>     
  43.             </list>     
  44.         </property>     
  45.     </bean>     
  46.     <!-- 聲明一個服務 -->   
  47.        
  48.     <bean id = "ticketServiceTarget"  class="com.easyway.ecache.service.TicketService" />     
  49.        
  50.     <!-- 相關的服務 -->   
  51.      <bean id="ticketService"  
  52.        class="org.springframework.aop.framework.ProxyFactoryBean">   
  53.        <property name="target">     
  54.             <ref local="ticketServiceTarget"/>     
  55.         </property>     
  56.         <property name="interceptorNames">     
  57.             <list>     
  58.                 <value>methodCachePointCut</value>     
  59.             </list>     
  60.         </property>     
  61.      </bean>   
  62.         
  63. </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.測試服務類:

Java代碼 複製代碼 收藏代碼
  1. package com.easyway.ecache.service;   
  2.   
  3. import java.util.List;   
  4. /**  
  5.  * 對其所有的以testMethod* ,getServiceName方式命令的方法,  
  6.  * 進行緩存處理。當調用其他命令時,不進行緩存  
  7.  * @author longgangbai  
  8.  *  
  9.  */  
  10. @SuppressWarnings("unchecked")   
  11. public class TicketService {   
  12.        
  13.     public String testMethod(){      
  14.         System.out.println("沒走緩存,直接調用TestService.testMethod()");      
  15.         return "china";      
  16.     }      
  17.           
  18.     public void updateMethod(){      
  19.         System.out.println("updateMethod");      
  20.     }      
  21.           
  22.     public void insertMethod(){      
  23.         System.out.println("insertMethod");      
  24.     }      
  25.           
  26.     public void deleteMethod(){      
  27.         System.out.println("deleteMethod");      
  28.     }         
  29.   
  30.     /**  
  31.      * 需要緩存的集合  
  32.      */  
  33.     private List ticketList;   
  34.     /**  
  35.      * 需要緩存的服務名稱  
  36.      */  
  37.     private String serviceName;   
  38.   
  39.     public String getServiceName() {   
  40.         return serviceName;   
  41.     }   
  42.   
  43.     public void setServiceName(String serviceName) {   
  44.         this.serviceName = serviceName;   
  45.     }   
  46.   
  47.     public List getTicketList() {   
  48.         return ticketList;   
  49.     }   
  50.     public void setTicketList(List ticketList) {   
  51.         this.ticketList = ticketList;   
  52.     }   
  53.     /**  
  54.      * 修改的服務端名稱備註但是不緩存  
  55.      * @param serviceName  
  56.      */  
  57.     public void changesetServiceName(String serviceName) {   
  58.         this.serviceName = serviceName;   
  59.     }   
  60. }  
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.測試用例

Java代碼 複製代碼 收藏代碼
  1. package com.easyway.ecache.service;   
  2.   
  3. import org.springframework.context.ApplicationContext;   
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;   
  5.      
  6. /**    
  7.  * 這裏使用了ehcache與spring結合,這裏並沒用用到數據庫,用spring只是用來管理bean,  
  8.  * 這裏用ehcache就相當於數據庫,存放對象信息    
  9.  *  @author longgangbai  
  10.  */     
  11.      
  12. @SuppressWarnings({"unchecked"})      
  13. public class HelloEhcacheSpring{      
  14.     public static void main(String[] args) {      
  15.         ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");      
  16.               
  17.         TicketService ticketSrv = (TicketService) context.getBean("ticketService");   
  18.            
  19.         //配置了spring就可以從配置文件裏找到對應的接口實現類,再生成實例對象,以完成業務處理      
  20.         String srvName0=ticketSrv.testMethod();   
  21.            
  22.         //獲取初始化服務端名稱   
  23.         System.out.println("srvName0="+srvName0);   
  24.            
  25.         //設置存儲的名稱   
  26.         ticketSrv.setServiceName("ticketService");   
  27.            
  28.         String srvName1=ticketSrv.testMethod();   
  29.            
  30.         //獲取服務端名稱   
  31.         System.out.println("srvName1="+srvName1);   
  32.            
  33.         //修改服務名稱但是不緩存   
  34.         ticketSrv.updateMethod();   
  35.            
  36.         String srvName2=ticketSrv.testMethod();   
  37.            
  38.         //獲取服務端名稱來源自緩存注意觀察   
  39.         System.out.println("srvName2="+srvName2);   
  40.           
  41.           
  42.     }      
  43.        
  44. }    
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

發佈了792 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章