1. 程式人生 > >Ehcache快取結合Spring AOP實現介面資料快取

Ehcache快取結合Spring AOP實現介面資料快取

下載安裝

JAVA程式碼塊

   首先我們要想好要實現什麼樣的功能,我們需要攔截service層的增刪改查介面然後以不同的動作對快取進行不同的操作:查詢、獲取、列表等資料從快取獲取,刪除、修改等資料刪除已有快取
  • 只新增快取和讀取快取

    建立JAVA類MethodPutCacheInterceptor(快取攔截器)該類只對針對get、load、list、search介面做快取和讀取資料

    public class MethodPutCacheInterceptor {
       private Cache ehCache;

    //通過spring注入方式例項化ehcache
public void setCache(Cache cache) { this.ehCache = cache; } //建構函式 public MethodPutCacheInterceptor() { super(); } //spring AOP 方法被呼叫時被攔截 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { //獲取類名 String targetName = pjp.getTarget().getClass().getName(); //獲取方法名
String methodName = pjp.getSignature().getName(); //獲取引數 Object[] atguments = pjp.getArgs(); Object result; //獲取cache key值 String cacheKey = getCacheKey(targetName, methodName, atguments); //通過key獲取快取value Element element = ehCache.get(cacheKey); if
(element == null) { logger.info("從DB讀取資料!"); //執行連線點裡面的方法 result = pjp.proceed(); //將連線點方法返回的資料新增到快取裡 element = new Element(cacheKey, (Serializable) result); ehCache.put(element); } else { logger.info("從快取讀取資料!"); } //最終返回快取資料 return element.getObjectValue(); } //spring aop 錯誤返回 public void doThrowing(JoinPoint jp, Throwable ex) { System.out.println("method " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + " throw exception"); System.out.println(ex.getMessage()); } /** * 獲得cache key的方法,cache key是Cache中一個Element的唯一識 * cache key包括 包名+類名+方法名 * @param targetName 類名 * @param methodName 方法名 * @param arguments 引數名 * @return String */ 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 (Object object : arguments) { sb.append(".").append(object); } } return sb.toString(); } }
  • 建立JAVA類MethodRemoveCacheInterceptor(快取攔截器)該類只對針對delete、remove、update介面做快取資料清除
public class MethodRemoveCacheInterceptor{
     private Cache ehCache;

     //通過spring注入方式例項化ehcache
     public void setCache(Cache cache) {
        this.ehCache = cache;
     }
     //建構函式
     public MethodPutCacheInterceptor() {
        super();
     }
   public void doAfter(JoinPoint jp) {
        //獲取類名
        String targetName = jp.getTarget().getClass().getName();
        //獲取快取key集合
        List list = ehCache.getKeys();
        for (Object key : list) {
            String cacheKey = String.valueOf(key);
            //判斷key中是否含有該類名
            if (cacheKey.startsWith(targetName)) {
                //存在則刪除該快取
                ehCache.remove(cacheKey);
            }
        }
    }
     public void doThrowing(JoinPoint jp, Throwable ex) {
        //獲取錯誤資訊
      System.out.println("method " +jp.getTarget().getClass().getName() + "." +      jp.getSignature().getName() + " throw exception");
      System.out.println(ex.getMessage());
    }
}

applicationContext.xml配置

<!--注入EhCacheManagerFactoryBean-->
<bean id="defaultCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <!--獲取ehcache.xml檔案-->
        <property name="configLocation">        
            <value>classpath:cache/ehcache.xml</value>
        </property>
    </bean>

<!--注入EhCacheFactoryBean-->
    <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <!--指定父類cacheManager的bean-->
        <property name="cacheManager" ref="defaultCacheManager" />
        <!--指定ehcache.xml快取名為userCache的快取配置,沒找到則預設為defaultCache-->
        <property name="cacheName">
            <value>userCache</value>
        </property>
    </bean>
    <!--將EhCacheFactoryBean注入到類MethodPutCacheInterceptor中cache物件中-->
    <bean id="methodPutCacheInterceptor" class="dome.core.tools.ehcache.MethodPutCacheInterceptor">
        <property name="cache" ref="ehCache" />
    </bean>

    <!--Spring AOP 實現配置-->
    <!--實現service層中get、list、find介面攔截-->
    <aop:config>
        <aop:aspect id="MethodPutCacheInterceptor" ref="methodPutCacheInterceptor">
            <aop:pointcut id="methodPutCacheService"
                expression="execution(* dome.core.*.service.*.get*(..)) or
                            execution(* dome.core.*.service.*.list*(..)) or 
                            execution(* dome.core.*.service.*.find*(..)))"></aop:pointcut>

            <aop:around pointcut-ref="methodPutCacheService" method="doAround" />
            <aop:after-throwing pointcut-ref="methodPutCacheService" method="doThrowing" throwing="ex" />
        </aop:aspect>
    </aop:config>

    <!--實現service層中delete、remove、update介面攔截-->
    <bean id="methodRemoveCacheInterceptor" class="dome.core.tools.ehcache.MethodRemoveCacheInterceptor">
        <property name="cache" ref="ehCache" />
    </bean>

    <aop:config>
        <aop:aspect id="MethodRemoveCacheInterceptor" ref="methodRemoveCacheInterceptor">
            <aop:pointcut id="methodRemoveCacheService"
                expression="execution(* dome.core.*.service.*.delete*(..)) or
                            execution(* dome.core.*.service.*.remove*(..)) or
                            execution(* dome.core.*.service.*.update*(..)))"></aop:pointcut>

            <aop:after pointcut-ref="methodRemoveCacheService" method="doAfter" />
            <aop:after-throwing pointcut-ref="methodRemoveCacheService" method="doThrowing" throwing="ex" />
        </aop:aspect>
    </aop:config>

ehcache.xml配置

<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 
    maxElementsInMemory="1000" 
    eternal="false"
    timeToIdleSeconds="120" 
    timeToLiveSeconds="120" 
    overflowToDisk="false" />

    <cache name="userCache" 
    maxElementsInMemory="10000" 
    eternal="false"
    overflowToDisk="true" 
    timeToIdleSeconds="300000" 
    timeToLiveSeconds="600000"
    memoryStoreEvictionPolicy="LFU">
    </cache>

</ehcache>

每當程式呼叫service層介面時,就會判斷介面名是否以get、list、find開頭,如果是則進入spring aop攔截中判斷該方法是否在快取中,如果存在則直接從快取獲取返回值,如果不存在則直接執行連線點方法(業務類或者dao層)然後將返回值放入快取並返回快取中的資料。
如果呼叫serivce層delete、update、remove開頭的介面,則進入攔截器判斷是否存在該快取,如果存在則刪除快取。