Spring搭配Ehcache實例解析

轉載請註明出處:http://blog.csdn.net/dongdong9223/article/details/50538085
本文出自【我是幹勾魚的博客css

1 Ehcache簡單介紹

EhCache 是一個純Java的進程內緩存框架。具備高速、精幹等特色,是Hibernate中默認的CacheProvider。java

Ehcache是一種普遍使用的開源Java分佈式緩存。主要面向通用緩存,Java EE和輕量級容器。它具備內存和磁盤存儲,緩存載入器,緩存擴展,緩存異常處理程序,一個gzip緩存servlet過濾器,支持REST和SOAP api等特色。git

Ehcache最初是由Greg Luck於2003年開始開發。github

2009年,該項目被Terracotta購買。軟件仍然是開源,但一些新的主要功能(好比,高速可從新啓動性之間的一致性的)僅僅能在商業產品中使用,好比Enterprise EHCache and BigMemory。web

維基媒體Foundationannounced眼下使用的就是Ehcache技術。spring

總之Ehcache仍是一個不錯的緩存技術,咱們來看看Spring搭配Ehcache是怎樣實現的。數據庫

2 Spring搭配Ehcache

系統結果例如如下:express

這裏寫圖片描寫敘述

3 詳細配置介紹

有這幾部分的結合:api

  • src:java代碼,包含攔截器,調用接口,測試類緩存

  • src/cache-bean.xml:配置Ehcache。攔截器,以及測試類等信息相應的bean

  • src/ehcache.xml:Ehcache緩存配置信息

  • WebRoot/lib:庫

4 詳細內容介紹

4.1 src

4.1.1 攔截器

代碼中首先配置了兩個攔截器:

第一個攔截器爲:

com.test.ehcache.CacheMethodInterceptor

內容例如如下:

package com.test.ehcache;

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.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

public class CacheMethodInterceptor implements MethodInterceptor, InitializingBean {

    private Cache cache;

    public void setCache(Cache cache) {
        this.cache = cache;
    }

    public CacheMethodInterceptor() {
        super();
    }

    /** * 攔截ServiceManager的方法,並查找該結果是否存在,假設存在就返回cache中的值, * 不然,返回數據庫查詢結果,並將查詢結果放入cache */
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //獲取要攔截的類
        String targetName = invocation.getThis().getClass().getName();
        //獲取要攔截的類的方法
        String methodName = invocation.getMethod().getName();
        //得到要攔截的類的方法的參數
        Object[] arguments = invocation.getArguments();
        Object result;

        //建立一個字符串。用來作cache中的key
        String cacheKey = getCacheKey(targetName, methodName, arguments);
        //從cache中獲取數據
        Element element = cache.get(cacheKey);

        if (element == null) {
        //假設cache中沒有數據,則查找非緩存,好比數據庫,並將查找到的放入cache

            result = invocation.proceed();
            //生成將存入cache的key和value
            element = new Element(cacheKey, (Serializable) result);
            System.out.println("-----進入非緩存中查找,好比直接查找數據庫,查找後放入緩存");
            //將key和value存入cache
            cache.put(element);
        } else {
        //假設cache中有數據,則查找cache

            System.out.println("-----進入緩存中查找,不查找數據庫。緩解了數據庫的壓力");
        }
        return element.getValue();
    }

    /** * 得到cache的key的方法,cache的key是Cache中一個Element的惟一標識, * 包含包名+類名+方法名,如:com.test.service.TestServiceImpl.getObject */
    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();
    }

    /** * implement InitializingBean,檢查cache是否爲空 70 */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache,
                "Need a cache. Please use setCache(Cache) create it.");
    }

}

CacheMethodInterceptor用來攔截以「get」開頭的方法,注意這個攔截器是先攔截,後運行原調用接口。

另外一個攔截器:

com.test.ehcache.CacheAfterReturningAdvice

詳細內容:

package com.test.ehcache;

import java.lang.reflect.Method;
import java.util.List;

import net.sf.ehcache.Cache;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

public class CacheAfterReturningAdvice implements AfterReturningAdvice, InitializingBean {

    private Cache cache;

    public void setCache(Cache cache) {
        this.cache = cache;
    }

    public CacheAfterReturningAdvice() {
        super();
    }

    public void afterReturning(Object arg0, Method arg1, Object[] arg2,
            Object arg3) throws Throwable {
        String className = arg3.getClass().getName();
        List list = cache.getKeys();
        for (int i = 0; i < list.size(); i++) {
            String cacheKey = String.valueOf(list.get(i));
            if (cacheKey.startsWith(className)) {
                cache.remove(cacheKey);
                System.out.println("-----清除緩存");
            }
        }
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache,
                "Need a cache. Please use setCache(Cache) create it.");
    }

}

CacheAfterReturningAdvice用來攔截以「update」開頭的方法,注意這個攔截器是先運行原調用接口,後被攔截。

4.1.2 調用接口

接口名稱爲:

com.test.service.ServiceManager

詳細內容例如如下:

package com.test.service;

import java.util.List;

public interface ServiceManager {  
    public List getObject();  

    public void updateObject(Object Object);  
}

實現類名稱爲:

com.test.service.ServiceManagerImpl

詳細內容例如如下:

package com.test.service; import java.util.ArrayList; import java.util.List; public class ServiceManagerImpl implements ServiceManager { @Override public List getObject() { System.out.println("-----ServiceManager:緩存Cache內不存在該element,查找數據庫,並放入Cache!

"); return null; } @Override public void updateObject(Object Object) { System.out.println("-----ServiceManager:更新了對象,這個類產生的cache都將被remove!"); } }

4.1.3 測試類

測試類名稱爲:

com.test.service.TestMain

詳細內容爲:

package com.test.service;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {

    public static void main(String[] args) {

        String cacheString = "/cache-bean.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(
                cacheString);
        //獲代替理工廠proxyFactory生成的bean,以便產生攔截效果
        ServiceManager testService = (ServiceManager) context.getBean("proxyFactory");

        // 第一次查找
        System.out.println("=====第一次查找");
        testService.getObject();

        // 第二次查找
        System.out.println("=====第二次查找");
        testService.getObject();

        // 運行update方法(應該清除緩存)
        System.out.println("=====第一次更新");
        testService.updateObject(null);

        // 第三次查找
        System.out.println("=====第三次查找");
        testService.getObject();
    } 
}

此處要注意。獲取bean是經過代理工廠proxyFactory生產的bean,這樣纔會有攔截效果。

能夠看出來,在測試類裏面設置了四次調用。運行順序爲:

  • 第一次查找
  • 第二次查找
  • 第一次更新
  • 第三次查找

4.2 src/cache-bean.xml

cache-bean.xml用來配置Ehcache。攔截器,以及測試類等信息相應的bean,內容例如如下:

<?xml version="1.0" encoding="UTF-8"?

> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- 引用ehCache 的配置--> <bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>ehcache.xml</value> </property> </bean> <!-- 定義ehCache的工廠,並設置所使用的Cache的name。即「com.tt」 --> <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <!-- Cache的名稱 --> <property name="cacheName"> <value>com.tt</value> </property> </bean> <!-- 建立緩存、查詢緩存的攔截器 --> <bean id="cacheMethodInterceptor" class="com.test.ehcache.CacheMethodInterceptor"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- 更新緩存、刪除緩存的攔截器 --> <bean id="cacheAfterReturningAdvice" class="com.test.ehcache.CacheAfterReturningAdvice"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- 調用接口,被攔截的對象 --> <bean id="serviceManager" class="com.test.service.ServiceManagerImpl" /> <!-- 插入攔截器。確認調用哪一個攔截器,攔截器攔截的方法名特色等,此處調用攔截器com.test.ehcache.CacheMethodInterceptor --> <bean id="cachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 增長切面。切面爲當運行完print方法後。在運行增長的切面 --> <property name="advice"> <ref local="cacheMethodInterceptor" /> </property> <property name="patterns"> <list> <!-- ### .表示符合不論什麼單一字元 ### +表示符合前一個字元一次或屢次 ### *表示符合前一個字元零次或屢次 ### \Escape不論什麼Regular expression使用到的符號 --> <!-- .*表示前面的前綴(包含包名),意思是表示getObject方法--> <value>.*get.*</value> </list> </property> </bean> <!-- 插入攔截器,確認調用哪一個攔截器,攔截器攔截的方法名特色等。此處調用攔截器com.test.ehcache.CacheAfterReturningAdvice --> <bean id="cachePointCutAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="cacheAfterReturningAdvice" /> </property> <property name="patterns"> <list> <!-- .*表示前面的前綴(包含包名),意思是updateObject方法--> <value>.*update.*</value> </list> </property> </bean> <!-- 代理工廠 --> <bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 說明調用接口bean名稱 --> <property name="target"> <ref local="serviceManager" /> </property> <!-- 說明攔截器bean名稱 --> <property name="interceptorNames"> <list> <value>cachePointCut</value> <value>cachePointCutAdvice</value> </list> </property> </bean> </beans>

各個bean的內容都作了凝視說明,值得注意的是,不要忘了代理工廠bean。

4.3 src/ehcache.xml

ehcache.xml中存儲Ehcache緩存配置的詳細信息,內容例如如下:

<?xml version="1.0" encoding="UTF-8"?>  
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <!-- 緩存文件位置 -->
    <diskStore path="D:\\temp\\cache" />  

    <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> 
    <!-- 定義緩存文件信息,當中「com.tt」爲緩存文件的名字 -->  
    <cache name="com.tt" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300000" timeToLiveSeconds="600000" overflowToDisk="true" />  
</ehcache>

能夠看到緩存的存儲的存儲位置設置爲「D:\temp\cache」。緩存名稱設置成了「com.tt」,如圖:

這裏寫圖片描寫敘述

4.4 WebRoot/lib

所需的java庫,詳見開頭的系統結構圖片,此處略。

5 測試

運行測試類,測試結果例如如下:

這裏寫圖片描寫敘述

經過運行結果咱們能夠看出:

第一次查找被攔截後發現是首次攔截,尚未緩存Cache。因此先運行一下原有接口類。獲得要查詢的數據,有多是經過數據庫查詢獲得的,而後再生成Cache,並將查詢獲得的數據放入Cache。

第二次查找被攔截後發現已經存在Cache。因而再也不運行原有接口類,也就是再也不查詢數據庫啦,直接經過Cache獲得查詢數據。固然這裏僅僅是簡單打印一下。

而後是第一次更新,被攔截後所作的操做是將Cache中的數據全部存入數據庫,並將Cache刪除。

最後是第三次查詢。被攔截後又發現系統不存在Cache。因而運行原接口類查詢數據庫,建立Cache。並將新查詢獲得的數據放入Cache。同第一次查詢的方式是同樣的。

至此咱們就實現了Spring搭配Ehcache所需要完畢的操做。

6 附件源碼

附件源碼能夠從個人github站點上獲取。

相關文章
相關標籤/搜索