`

[转]利用Spring的AOP机制配置管理EHCache

    博客分类:
  • Java
阅读更多
      如果我们的项目中采用的是Spring+hibernate来构建的,在缓存方面,我们一定会首先想到Spring自带的EHCache缓存工具,在Spring中集成了目前比较流行的缓存策略EHCache,现在用的比较多的还有像OSCache,MemCached.这些应该是当前用的最多的缓存工具了。
       在Spring+hibernate的这样的框架中,EHCache应该属于二级缓存了,我们知道在Hibernate中已经默认的使用了一级缓存,也就是在Session中。二级缓存应该是SessionFactory的范围了。二级缓存默认不会起作用的,这就需要我们简单的配置一下就可以了。
       在配置之前,我先说明一点,缓存从理论上来说是可以提高你网站系统的性能,但前提就是你要保证你有一个良好的架构设计。比如用Spring+Hibernate构建的系统,如果用单个服务器,用Spring自带的EHCache来做二级缓存是再好不过了。如果你的系统是分布式的系统,有多台服务器,那么MemCached是最好的选择了,一般来说MemCached在做缓存这一块,要比EHCache和OSCache的性能要好点,但是并不是所有的网站用MemCached都能达到事半功倍的,它虽然是比较好,但它有一个前提,那就是你有多台服务器,是分布式的。这样用MemCached对系统的性能一定OK。因为Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用, MemCached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源 .OSCache这个缓存机制的限制就比较少了。它和EHCache差不多。
        在Spring+Hibernate中整合EHCache只需简单的三步。
        第一步:配置缓存文件ehcache.xml,默认放到src目录下。下面是简单的配置。
<ehcache>
    <!—设置缓存文件 .data 的创建路径。
    如果该路径是 Java 系统参数,当前虚拟机会重新赋值。
    下面的参数这样解释:
        user.home       – 用户主目录
        user.dir        – 用户当前工作目录
        java.io.tmpdir  – 默认临时文件路径,就是在tomcat的temp目录
    -->
    <diskStore path="java.io.tmpdir"/>

    <!—缺省缓存配置。CacheManager 会把这些配置应用到程序中。
    下列属性是 defaultCache 必须的:
        maxInMemory           - 设定内存中创建对象的最大值。
        eternal               - 设置元素(译注:内存中对象)是否永久驻留。
                                如果是,将忽略超时限制且元素永不消亡。
        timeToIdleSeconds     - 设置某个元素消亡前的停顿时间。
                                也就是在一个元素消亡之前,两次访问时间的最大时间间隔值。
                                这只能在元素不是永久驻留时有效(译注:如果对象永恒不灭,则
                                设置该属性也无用)。
                                如果该值是 0 就意味着元素可以停顿无穷长的时间。
        timeToLiveSeconds     - 为元素设置消亡前的生存时间。
                                也就是一个元素从构建到消亡的最大时间间隔值。
                                这只能在元素不是永久驻留时有效。
        overflowToDisk        - 设置当内存中缓存达到 maxInMemory 限制时元素是否可写到磁盘上。
    -->
    <!--timeToLiveSeconds的时间一定要大于等于timeToIdleSeconds的时间按-->
    <cache name="DEFAULT_CACHE"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="500"
           timeToLiveSeconds="500"
           overflowToDisk="true"
           />
</ehcache>

  上面有一个默认的缓存配置,还有一个我们自己配置的缓存,在应用程序中如果不指明缓存的话,就会默认的使用默认的配置属性。
    第二步:用Spring中的强大机制,面向切面的设计AOP.来编写两个类文件,MethodCacheAfterAdvice.java(主要是对脏东西的同步更新)和MethodCacheInterceptor.java(主要使用拦截器来为要缓存的对象建立缓存并缓存)。拦截器的实现机制其实就是我们常用的过滤器。它和过滤器的工作原理一样。以下是这两个文件。
MethodCacheInterceptor.java
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;
    }

    public MethodCacheInterceptor() {
        super();
    }

    /**
     * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在就返回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;
        logger.debug("Find object from cache is " + cache.getName());
        String cacheKey = getCacheKey(targetName, methodName, arguments);
        Element element = cache.get(cacheKey);
        long startTime = System.currentTimeMillis();
        if (element == null) {
            logger.debug("Hold up method , Get method result and create cache........!");
            result = invocation.proceed();
            element = new Element(cacheKey, (Serializable) result);
            cache.put(element);
            long endTime = System.currentTimeMillis();
            logger.info(targetName + "." + methodName + " 方法被首次调用并被缓存。耗时"
                    + (endTime - startTime) + "毫秒" + " cacheKey:"
                    + element.getKey());
        } else {
            long endTime = System.currentTimeMillis();
            logger.info(targetName + "." + methodName + " 结果从缓存中直接调用。耗时"
                    + (endTime - startTime) + "毫秒" + " cacheKey:"
                    + element.getKey());
        }
        return element.getValue();
    }

    /**
     * 获得cache key的方法,cache key是Cache中一个Element的唯一标识 cache key包括 包名+类名+方法名+参数
     */
    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是否为空
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache,
                "Need a cache. Please use setCache(Cache) create it.");
    }
}

   这个方法实现了两个接口,一个是MethodInterceptor(方法拦截),它主要是在方法的调用前后都可以执行。另一个InitializingBean (初始化Bean)它主要在方法调用之后做一下简单的检查,主要实现写在afterPropertiesSet()中,就可以了 。
MethodCacheAfterAdvice .java
public class MethodCacheAfterAdvice implements AfterReturningAdvice,
        InitializingBean {

    private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);
    private Cache cache;

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

    public MethodCacheAfterAdvice() {
        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);
                logger.debug("remove cache " + cacheKey);
            }
        }
    }

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

  这个方法主要是保证缓存的同步,保持与数据库的数据一致性。
第三步:配置Bean了,applicationContext-ehcache.xml文件就是Spring中的Ioc(控制反转容器)的描述了。上面的只是简单的写了两个方法,具体的能起到什么作用,以及何时起作用,以及怎样用声明式的方式(AOP)和Bean结合。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="[url]http://www.springframework.org/schema/beans[/url]"
       xmlns:xsi="[url]http://www.w3.org/2001/XMLSchema-instance[/url]"
       xsi:schemaLocation="[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]">
    <!-- 利用BeanNameAutoProxyCreator自动创建事务代理 -->
    <bean id="transactionInterceptor"
          class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="transactionManager" />
        </property>
        <!-- 配置事务属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>
    
    <!-- 引用ehCache的配置 -->
    <bean id="defaultCacheManager"
          class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation">
            <value>classpath:ehcache.xml</value>
        </property>
    </bean>
    <!-- 定义ehCache的工厂,并设置所使用的Cache name -->
    <bean id="ehCache"
          class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager">
            <ref local="defaultCacheManager" />
        </property>
        <property name="cacheName">
            <value>DEFAULT_CACHE</value>
        </property>
    </bean>
    <!-- find/create cache拦截器 -->
    <bean id="methodCacheInterceptor"
          class="com.w3cs.cache.ehcache.MethodCacheInterceptor">
        <property name="cache">
            <ref local="ehCache" />
        </property>
    </bean>
    <!-- flush cache拦截器 -->
    <bean id="methodCacheAfterAdvice"
          class="com.w3cs.cache.ehcache.MethodCacheAfterAdvice">
        <property name="cache">
            <ref local="ehCache" />
        </property>
    </bean>
    <bean id="methodCachePointCut"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref local="methodCacheInterceptor" />
        </property>
        <property name="patterns">
            <list>
                <value>.*find.*</value>
                <value>.*get.*</value>
            </list>
        </property>
    </bean>
    <bean id="methodCachePointCutAdvice"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref local="methodCacheAfterAdvice" />
        </property>
        <property name="patterns">
            <list>
                <value>.*create.*</value>
                <value>.*update.*</value>
                <value>.*delete.*</value>
            </list>
        </property>
    </bean>
    <!-- 自动代理 -->
    <bean id="autoproxy"
          class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 可以是Service或DAO层(最好是针对业务层*Service) -->
        <property name="beanNames">
            <list>
                <value>*DAO</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>methodCachePointCut</value>
                <value>methodCachePointCutAdvice</value>
                <value>transactionInterceptor</value>
            </list>
        </property>
    </bean>
</beans>


        上面我是针对DAO层进行拦截并缓存的,最好是能在业务层进行拦截会更好,你可以根据你的系统具体的设计,如果没有业务层的话,对DAO层拦截也是可以的。拦截采用的是用正规表达式配置的。对find,get的方法只进行缓存,如果 create,update,delete方法进行缓存的同步。对一些频繁的操作最好不要用缓存,缓存的作用就是针对那些不经常变动的操作。
         只需这简单的三部就可以完成EHCache了。最好亲自试一试。我并没有针对里面对方法过细的讲解。其实都很简单,多看看就会明白了。不当之处,敬请原谅。

上文出自 “魏杰的技术专栏” 博客,请务必保留此出处

附:ehcache配置文件内容详解:
maxElementsInMemory :cache 中最多可以存放的元素的数量。如果放入cache中的元素超过这个数值,有两种情况:
1. 若overflowToDisk的属性值为true,会将cache中多出的元素放入磁盘文件中。
2. 若overflowToDisk的属性值为false,会根据memoryStoreEvictionPolicy的策略替换cache中原有的元素。

eternal :是否永驻内存。如果值是true,cache中的元素将一直保存在内存中,不会因为时间超时而丢失,所以在这个值为true的时候,timeToIdleSeconds和timeToLiveSeconds两个属性的值就不起作用了。


3. timeToIdleSeconds :访问这个cache中元素的最大间隔时间。如果超过这个时间没有访问这个cache中的某个元素,那么这个元素将被从cache中清除。


4. timeToLiveSeconds : cache中元素的生存时间。意思是从cache中的某个元素从创建到消亡的时间,从创建开始计时,当超过这个时间,这个元素将被从cache中清除。


5. overflowToDisk :溢出是否写入磁盘。系统会根据标签<diskStore path="java.io.tmpdir"/> 中path的值查找对应的属性值,如果系统的java.io.tmpdir的值是 D:\temp,写入磁盘的文件就会放在这个文件夹下。文件的名称是cache的名称,后缀名的data。如:CACHE_FUNC.data。


6. diskExpiryThreadIntervalSeconds  :磁盘缓存的清理线程运行间隔.


7. memoryStoreEvictionPolicy :内存存储与释放策略。有三个值:
LRU -least recently used
LFU -least frequently used
FIFO-first in first out, the oldest element by creation time


diskPersistent : 是否持久化磁盘缓存。当这个属性的值为true时,系统在初始化的时候会在磁盘中查找文件名为cache名称,后缀名为index的的文件,如 CACHE_FUNC.index 。这个文件中存放了已经持久化在磁盘中的cache的index,找到后把cache加载到内存。要想把cache真正持久化到磁盘,写程序时必须注意,在是用net.sf.ehcache.Cache的void put (Element element)方法后要使用void flush()方法。
更多说明可看ehcache自带的ehcache.xml的注释说明.
分享到:
评论
1 楼 huangfeng555 2011-03-20  

相关推荐

    Spring AOP+ehCache简单缓存系统解决方案

    结合Spring AOP和ehCache,我们可以创建一个自动化的缓存管理机制。在Spring配置中,我们可以定义一个切面,该切面会在特定的方法调用前后执行。例如,当方法调用前,我们可以检查ehCache中是否存在所需的数据,如果...

    Spring中AOP实现EHCache的整合(一)

    在本文中,我们将深入探讨如何在Spring框架中集成并使用AOP(面向切面编程)来实现对EHCache的高效管理。Spring是一个广泛使用的Java应用框架,它提供了强大的依赖注入和面向切面编程功能。而EHCache是一款流行、高...

    SpringAOP结合ehCache实现简单缓存实例

    在IT行业中,Spring AOP(面向切面编程)和EhCache是两个非常重要的概念,它们在提升应用程序性能和管理缓存方面发挥着关键作用。本文将深入探讨如何结合Spring AOP与EhCache实现一个简单的缓存实例,以便优化Java...

    Spring AOP+ehCache简单缓存系统解决方案.doc

    为了实现这一目标,我们可以利用 Spring AOP(面向切面编程)的拦截器机制。 AOP 允许我们在不改变原有代码的情况下,向方法调用添加额外的行为。在这里,我们创建了一个名为 `MethodCacheInterceptor` 的类,它...

    Spring+Ehcache集成

    最后,我们利用Spring AOP实现方法级别的缓存注解。首先定义一个自定义注解,例如`@CacheableMethod`: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Cacheable(value =...

    spring,spring mvc,hibernate,ehcache JavaWeb后台框架

    在Java Web开发领域,Spring、Spring MVC、Hibernate和Ehcache是四个非常关键的框架,它们共同构建了一个强大且高效的后台开发环境。下面将详细解释这些框架的核心功能和使用方式。 1. **Spring框架**:Spring是...

    Spring中AOP实现EHCache的整合中采用SpringModule结合(二)

    本篇文章将深入探讨如何在Spring中通过AOP来整合EHCache,特别是在使用Spring Modules的情况下。 首先,我们需要理解Spring Modules(SpringModule)项目,这是一个为Spring框架提供额外功能的开源项目,它包含了对...

    spring,spring mvc,hibernate,ehcache Java后台框架

    Spring负责整体的控制流程和依赖管理,Spring MVC处理Web交互,Hibernate处理数据持久化,而Ehcache则作为缓存层提升应用响应速度。这种架构模式在现代企业级应用开发中被广泛采用,是Java开发者的必备知识。理解并...

    spring3 hibernate4 ehcache实例

    Spring作为轻量级的IoC(Inversion of Control)和AOP(Aspect Oriented Programming)容器,提供了一个统一的管理组件的方式,包括数据访问、事务管理等。Hibernate则是一个强大的ORM(Object-Relational Mapping)...

    Spring 与Ehcache实现基于方法的缓存

    本篇文章将详细探讨如何在Spring框架中集成并实现基于方法的缓存机制,利用Ehcache来优化数据访问。 首先,我们需要理解Spring的AOP概念,AOP允许我们定义横切关注点,如日志、事务管理或,正如在这个案例中,缓存...

    Ehcache集成Spring的使用(转载)

    3. **配置 Spring**: 在 Spring 的配置文件中,如 `applicationContext.xml`,添加 Ehcache 的配置: ```xml &lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&gt; ...

    Spring+Hibernate+ehcache整合

    1. **Spring配置**:Spring的配置文件(如`applicationContext.xml`)会定义bean,包括数据源、SessionFactory(Hibernate)、缓存管理器(Ehcache)以及业务层和服务层的组件。通过依赖注入,Spring将这些组件装配...

    SpringMVC+Mybatis+Spring+Shiro+ehcache整合配置文件

    Ehcache可以很好地与Spring集成,通过简单的配置即可实现缓存管理。 在整合这些技术时,首先需要在Spring的配置文件中定义bean,包括数据源、SqlSessionFactory(Mybatis的核心)、MapperScannerConfigurer(扫描...

    spring、 spring mvc、 hibernate、 ehcache Java后台框架

    Spring的核心特性包括依赖注入(DI)和面向切面编程(AOP),通过这些特性,开发者可以更加灵活地管理对象的生命周期和依赖关系,减少代码间的耦合。此外,Spring还提供了丰富的模块,如Spring JDBC、Spring ORM...

    aop例子aop例子

    总结来说,这个例子展示了如何在Spring中利用AOP进行功能扩展,如日志记录和缓存管理。通过定义切面和通知,我们可以将这些通用的非业务逻辑从主代码中解耦,使得代码更加整洁,维护性更强。同时,通过EhCache的配置...

    spring mvc + mybatis + ehcache

    Spring框架对Ehcache有很好的支持,可以通过`&lt;ehcache:config&gt;`标签进行配置,并通过`@Cacheable`等注解实现方法级别的缓存控制。 集成这三个框架,首先要在Spring的配置文件中定义DataSource、SqlSessionFactory和...

    spring+ibatis+ehcache整合例子

    在IT行业中,Spring、iBatis和Ehcache是三个非常重要的开源框架,它们分别用于企业级应用的依赖注入、数据库操作和缓存管理。这个"spring+ibatis+ehcache整合例子"是一个完整的示例项目,展示了如何将这三个框架无缝...

    Spring AOP应用

    1. **MyBatis拦截器**:Spring AOP可以和MyBatis的拦截器机制结合,对MyBatis的SqlSession或Mapper接口调用进行拦截,实现上述的动态数据源切换或缓存操作。 2. **事务管理**:使用Spring的`@Transactional`注解...

    spring + ehcache + redis两级缓存

    在Spring中,我们可以配置Ehcache作为缓存 provider,通过注解或XML配置来启用和管理缓存。 **Redis** 是一个高性能的键值数据库,常被用作分布式缓存系统。相比于Ehcache,Redis支持更丰富的数据结构(如字符串、...

Global site tag (gtag.js) - Google Analytics