Spring集成ehcache

我的环境是:ehcache-2.10.1.jar,spring4.0.6。
1.首先要准备好Jar包
http://www.ehcache.org/下载,如果是maven的话:

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.1</version>
    <type>pom</type>
</dependency>

2.接下来配置spring xml:

<!-- 配置ehcache -->
<!-- 开启ehcache注解 -->
<cache:annotation-driven cache-manager="cacheManager" /> 

<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="classpath:ehcache.xml" />
</bean>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="cacheManagerFactory" />
    </bean>

    <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager">
            <ref local="cacheManagerFactory" />
        </property>
        <property name="cacheName">
            <value>DEFAULT_CACHE</value>
        </property>
    </bean>   

    <!-- 创建缓存、查询缓存的拦截器 -->  
    <bean id="cacheMethodInterceptor" class="net.kehwa.tb.board.core.ehcache.CacheMethodInterceptor">  
        <property name="cache">  
            <ref local="ehCache" />  
        </property>  
    </bean>

    <!-- 更新缓存、删除缓存的拦截器 -->  
    <bean id="cacheAfterReturningAdvice" class="net.kehwa.tb.board.core.ehcache.CacheAfterReturningAdvice">  
        <property name="cache">  
            <ref local="ehCache" />  
        </property>  
    </bean>
    <!-- 插入拦截器,确认调用哪个拦截器,拦截器拦截的方法名特点等,此处调用拦截器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>.*query.*</value> 
                <value>.*get.*</value>
                <value>.*find.*</value>
            </list>  
        </property>  
    </bean>  

    <!-- 插入拦截器,确认调用哪个拦截器,拦截器拦截的方法名特点等,此处调用拦截器CacheAfterReturningAdvice -->
    <bean id="cachePointCutAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
        <property name="advice">  
            <ref local="cacheAfterReturningAdvice" />  
        </property>  
        <property name="patterns">  
            <list>
                <!-- .*表示前面的前缀(包括包名),意思是updateObject方法--> 
                <value>.*delete.*</value>
                <value>.*update.*</value>
                <value>.*add.*</value>
                <value>.*save.*</value>
                <value>.*edit.*</value>
            </list>  
        </property>  
    </bean> 

    <!-- 代理工厂 org.springframework.aop.framework.ProxyFactoryBean 只拦截一个 -->
    <bean id="proxyFactory" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 说明调用接口bean名称 -->
        <property name="beanNames">
           <list>
               <value>*Service</value>
           </list>
        </property>  
        <!-- 说明拦截器bean名称 -->
        <property name="interceptorNames">  
            <list>  
                <value>cachePointCut</value>  
                <value>cachePointCutAdvice</value>  
            </list>  
        </property> 
    </bean> 

3.配置ehcache.xml,我在spring xml中设置了ehcache.xml的位置在src某目录下:

<?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">
    <diskStore path="d:/cachetmpdir" /> <!--  java.io.tmpdir 默认临时文件路径    user.dir 用户当前工作目录    user.home 用户主目录 -->

    <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />

    <cache name="daoCache" 
        maxElementsInMemory="10000"
        maxElementsOnDisk="1000" 
        eternal="false" 
        overflowToDisk="true"
        diskSpoolBufferSizeMB="20" 
        timeToIdleSeconds="300" 
        timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LFU" />

    <!-- 
        name:缓存名称。通常为缓存对象的类名(非严格标准)。 
        maxElementsInMemory:设置基于内存的缓存可存放对象的最大数目。 
        maxElementsOnDisk:设置基于硬盘的缓存可存放对象的最大数目。  
        eternal:如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false; 
        timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,
            这个对象就会过期。当对象过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态。 
        timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,
            这个对象就会过期。当对象过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。
            timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义。 
        overflowToDisk:如果为true,表示当基于内存的缓存中的对象数目达到了maxElementsInMemory界限后,会把益出的对象写到基于硬盘的缓存中。
            注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。 
        memoryStoreEvictionPolicy:缓存对象清除策略。有三种: 
        1 FIFO ,first in first out ,这个是大家最熟的,先进先出,不多讲了 
        2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。 
        2 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,
            而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 
     -->
</ehcache>

4.接下来是编写我们的拦截器了,注意我的拦截器的包和你拦截器所在的包不一样,请在spring xml里做修改。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

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, result);
            System.out.println(cacheKey + "-----进入非缓存中查找,例如直接查找数据库,查找后放入缓存");
            // 将key和value存入cache
            cache.put(element);
        } else {
            // 如果cache中有数据,则查找cache
            System.out.println(cacheKey + "-----进入缓存中查找,不查找数据库,缓解了数据库的压力");
        }
        return element.getObjectValue();
    }

    /**
     * 获得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
     * 此方法会在所有属性都set之后调用
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
    }
}
// 接下来是清除缓存的
package net.kehwa.tb.board.core.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.");
    }

}
  1. 对于某一些特别的,不符合我们命名规范的方法,我们不想写在拦截器里,但是却想缓存对它进行处理。那么我们需要对相应的实现类(不是接口)进行注解:
    @Cacheable(value="daoCache")
    public List<CyclicalLife> totalCyclicalLifeForm(String date) {
        return cycDao.totalCyclicalLifeForm(date);
    }

@Cacheable(value=”daoCache”)注解表示我们需要缓存它,一般的我们会设置一个key值来确保其在缓存中更好的使用。
@CacheEvict(value=”daoCache”,allEntries=true) 注解表示我们需要清理缓存,allEntries=true表示清理全部缓存。

注解名描述
CacheableAnnotation indicating that the result of invoking a method (or all methods in a class) can be cached.
CacheConfig@CacheConfig provides a mechanism for sharing common cache-related settings at the class level.
CacheEvictAnnotation indicating that a method (or all methods on a class) triggers a cache evict operation.
CachePutAnnotation indicating that a method (or all methods on a class) triggers a cache put operation.
CachingGroup annotation for multiple cache annotations (of different or the same type).
EnableCachingEnables Spring’s annotation-driven cache management capability, similar to the support found in Spring’s XML namespace.

详情请看spring注解方面的文档。
需要注意的是,如果没有配置keyGenerator,那么Cacheable的默认key是”“,这样的话就必须手工指定一个了。

<bean id="userKeyGenerator" class="net.kehwa.tb.board.core.ehcache.UserKeyGenerator" />
<cache:annotation-driven cache-manager="cacheManager" key-generator="userKeyGenerator"/>

userKeyGenerator是我们自己的类:

package net.kehwa.tb.board.core.ehcache;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;

public class UserKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        // 获取要拦截的类
        String targetName = target.getClass().getName();
        // 获取要拦截的类的方法
        String methodName = method.getName();
        StringBuffer sb = new StringBuffer();
        sb.append(targetName).append(".").append(methodName);
        if ((params != null) && (params.length != 0)) {
            for (int i = 0; i < params.length; i++) {
                sb.append(".").append(params[i]);
            }
        }
        return sb.toString();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值