浅浅的议一下Spring整合EhCache的两种方式


placeholder image
admin 发布于:2015-01-08 23:02:00
阅读:loading

基本介绍

如果项目里面使用的有spring环境,除了可以使用传统的将缓存API封装进行调用外,在做缓存时还可以考虑使用spring整合的方式,如果是对传统的使用方式感兴趣,可以绕会上一篇日志《浅浅的对传统的ehcache换成的封装》了,这里主要说的是与spring整合的方式,有点废话了。

在查了与spring整合的方式后,大概了解到有两种方式,第一种是使用google为spring与ehcache提供的封装包ehcache-spring-annotations,我们可以在googlecode中下载,顺便给出下载地址吧https://code.google.com/p/ehcache-spring-annotations/ 在这个项目的主页上能直接看到使用maven环境构建需要的gav坐标,我将其坐标依赖的jar下载下来所有的jar如下图所示:

image.png

可以发现它里面的spring版本略老了,经使用上述的jar构建项目时发现spring所依赖的log4j没有,spring的cache工工厂类不知道在哪里,偷个懒我就直接将上面的spring相关的jar给删了,换成4.0.5(由于本地有个测试工程使用的是这个版本,昨天也正好找到此版本中ehcache的工厂类所在的jar包,故我们还需要将spring-context-support-4.0.5.RELEASE.jar加入项目中来,其他的jar包经测试发现使用此dependency下载下来的包就可以;当然你也可以在此项目的主页找到download,将此项目的开发包给下载下来,下载到的包括源代码等等,不包括它所依赖的其他的jar了,需要手动添加,下载页面如下:

image.png

可以发现最后一个版本是2011年的了,第二种方式是后续版本的spring自己提供的对此组件的封装库,这里且称两种方式分别为google的方式和spring的方式,所有调整后的工程lib中文件为:

image.png

为了整合测试方便,上述jar中同时包括了使用google和ehcache两种方式的依赖,先说一下先后顺序,从新建一个独立的Project开始。
1)新建一个Java Project,没什么好说的,将所有jar引入到工程当中,新建spring的配置文件和ehcache的配置文件,log4j的配置文件可有可无,不作为本次关注重点,东西越少越好。
2)各种配置好之后,可以稍微搞个main函数验证一下spring工程是否搭建有问题。
3)编写google方式的Service、Test以及说明
4)编写spring方式的Service、Test以及说明
5)。。。。。。 
 不废话了

一、google的方式
1、从applicationContext.xml开始

image.png

2、ehcache.xml

<?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" dynamicConfig="true">

    

    <!--设置缓存文件 .data 的创建路径。如果该路径是 Java 系统参数,当前虚拟机会重新赋值。

    下面的参数这样解释:

    user.home – 用户主目录

    user.dir      – 用户当前工作目录

    java.io.tmpdir – 默认临时文件路径  地址类似于:C:\Users\chendd\AppData\Local\Temp

    -->

    <diskStore path="java.io.tmpdir"/>

    <!--

        name:缓存名称。  

        maxElementsInMemory:缓存最大个数。  

        eternal:对象是否永久有效,一但设置了,timeout将不起作用。  

        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒),也就是说如果有一个缓存有多久没有被访问就会被销毁,如果该值是 0 就意味着元素可以停顿无穷长的时间。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。  

        timeToLiveSeconds"缓存有效时间设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。  

        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。  

        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。  

        maxElementsOnDisk:硬盘最大缓存个数。  

        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.  

        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。  

        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。  

        clearOnFlush:内存数量最大时是否清除。  

    -->

    <!--   默认的Cache配置。用来实现CacheManager.add(String cacheName)创建的缓存   -->

    <defaultCache  

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="120"

        timeToLiveSeconds="120"

        overflowToDisk="true"

        maxElementsOnDisk="10000000"

        diskPersistent="false"

        diskExpiryThreadIntervalSeconds="120"

        memoryStoreEvictionPolicy="LRU"

    />

    <cache name="system-common"  maxElementsInMemory="1000"

        eternal="false"

        timeToIdleSeconds="120"

        timeToLiveSeconds="120" 

        overflowToDisk="true"

        maxElementsOnDisk="10000"

        diskPersistent="false"

        diskExpiryThreadIntervalSeconds="120"

        memoryStoreEvictionPolicy="LRU"/>

</ehcache>

3、google方式的Service类

 

package com.cache.google;

import java.util.ArrayList;

import java.util.List;

import org.springframework.stereotype.Service;

import com.googlecode.ehcache.annotations.Cacheable;

import com.googlecode.ehcache.annotations.TriggersRemove;

import com.vo.User;

/**

 * 用户信息Service,使用google封装的缓存注解

 * @author chendd

 *

 */

@Service

public class UserCacheGoogleService {

    static List<User> dataList = new ArrayList<User>();

    static{

        dataList.add(new User(1001 , "chendd" , "cdd"));

        dataList.add(new User(1002 , "jiangjj" , "jjj"));

    }

    

    @Cacheable(cacheName = "system-common")

    public List<User> loadAllUsers(){

        

        System.out.println("************************发送查询 sqlselect * from user");

        return dataList;

    }

    

    @TriggersRemove(cacheName = "system-common" , removeAll = true)

    public void insertUser(){

        

        System.out.println("************************发送新增 sqlinsert into user values (?,?,?)");

        dataList.add(new User(1003 , "zengxr" , "zxr"));

    }

    

    @TriggersRemove(cacheName = "system-common" , removeAll = true)

    public void deleteUser(){

        

        System.out.println("************************发送删除 sqldelete from user where id = ?");

        dataList.remove(1);//删除第二条数据

    }

    

}

注意在上面代码上有个loadAllUsers函数,函数上面有com.googlecode.ehcache.annotations.Cacheable类型的注解,此注解的意思是将此函数作为缓存的业务函数,cacheName参数是指缓存的名称,与ehcache.xml中配置的cache标签name一致,很简单把,只需要在需要缓存的函数上标注就可以了,同理在新增、修改、删除等影响数据库数据时我们需要更新缓存,保持缓存里面的数据是同步的,故使用com.googlecode.ehcache.annotations.TriggersRemove注解,removeAll=true的意思是删除此缓存名称下的所有节点,when参数在实际使用中也需要注意,可以设置是在业务方法调用之前和之后触发,假设业务方法出现异常,而设置换成是在after之后执行时就会出现问题。
4、google方式的Test类 

package com.cache.test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.cache.spring.UserCacheSpringService;

public class UserCacheSpringServiceTest {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(

                "classpath:applicationContext.xml");

        UserCacheSpringService userService = context

                .getBean(UserCacheSpringService.class);

        System.out.println("首先查询一下所有用户列表,此时缓存当中是没有的,应该要发送sql语句");

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());

        System.out.println("缓存里面已经有数据了,后面再执行查询不应该再发送sql就可以拿到数据了");

        System.out.println("注意是否发送查询sql,不应该发送:"

                + userService.loadAllUsers().size());// 调用查询函数,从缓存中读取

        System.out.println("删除一条数据,再查询时需要从数据库中读取新的数据");

        userService.deleteUser();

        System.out.println("注意是否发送查询sql,应该要发送:");// 调用查询函数,从数据库中读取,发送sql

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());// 调用查询

        System.out.println("注意是否发送查询sql,查询完再查询一次,不应该发送:");// 调用查询函数,从数据库中读取,发送sql

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());// 调用查询

    }

}

 相应输出的打印如下:

image.png

二、spring的方式
1、配置文件

     相关的applicationContext.xml和enache.xml同上面共用。
2、spring方式的Service类

package com.cache.spring;

import java.util.ArrayList;

import java.util.List;

import org.springframework.cache.annotation.CacheEvict;

import org.springframework.cache.annotation.CachePut;

import org.springframework.cache.annotation.Cacheable;

import org.springframework.stereotype.Service;

import com.vo.User;

/**

 * 用户信息Service,使用google封装的缓存注解

 * 

 * @author chendd

 * 

 */

@Service

public class UserCacheSpringService {

    static List<User> dataList = new ArrayList<User>();

    static {

        dataList.add(new User(1001, "chendd""cdd"));

        dataList.add(new User(1002, "jiangjj""jjj"));

    }

    @Cacheable(value = "system-common")

    public List<User> loadAllUsers() {

        System.out

                .println("************************发送查询 sqlselect * from user");

        return dataList;

    }

    public void insertUser() {

        System.out

                .println("************************发送新增 sqlinsert into user values (?,?,?)");

        dataList.add(new User(1003, "zengxr""zxr"));

    }

    @CacheEvict(value = "system-common", allEntries = true)

    public void deleteUser() {

        System.out

                .println("************************发送删除 sqldelete from user where id = ?");

        dataList.remove(1);// 删除第二条数据

    }

}

如上代码中,
org.springframework.cache.annotation.Cacheable注解,value属性与google方式的cacheName属性一样,key表示换成的节点键的生成规则中的一部分,condition是它里面的表达式语言。org.springframework.cache.annotation.CacheEvict注解中的其他属性同上,beforeInvocation相当于google的when,allEntries相当于removeAll属性。
 org.springframework.cache.annotation.CachePut注解我表示最不好理解了,我认为此注解出现场合是根据特定规则的缓存key去更新已经存在的缓存中的某一个对象,它作为每次都执行的函数,但是执行完毕后会将返回值和此注解的key声明更改到缓存中去,比如,我先查询出一条记录A,将A放入缓存当中,后续对A做了更改后它会将更改后的值覆盖之前缓存的A的值,即时我们再调用查询A的函数时,也不会再去调用查询A对象的具体函数了,即不会再调用业务方法了。也就是说更新完后再调用查询函数(查询的缓存key与此更新的key一致),发现并刽发送sql或者说调用业务方法,但缓存中已经更新了;另外如果此注解标记的函数没有返回值,则更新后再从查询函数中获取的数据则为null。这里顺便给出对于数据库操作时的验证代码,前面说它更适合处理针对于一条数据或者已存在特定key数据处理,如下代码示例,更多测试代码都会上传到附件上:

Service 

image.png

Test

image.png

以上代码运行结果为:

image.png

3、spring方式的Test类

 

package com.cache.test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.cache.spring.UserCacheSpringService;

public class UserCacheSpringServiceTest {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(

                "classpath:applicationContext.xml");

        UserCacheSpringService userService = context

                .getBean(UserCacheSpringService.class);

        System.out.println("首先查询一下所有用户列表,此时缓存当中是没有的,应该要发送sql语句");

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());

        System.out.println("缓存里面已经有数据了,后面再执行查询不应该再发送sql就可以拿到数据了");

        System.out.println("注意是否发送查询sql,不应该发送:"

                + userService.loadAllUsers().size());// 调用查询函数,从缓存中读取

        System.out.println("删除一条数据,再查询时需要从数据库中读取新的数据");

        userService.deleteUser();

        System.out.println("注意是否发送查询sql,应该要发送:");// 调用查询函数,从数据库中读取,发送sql

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());// 调用查询

        System.out.println("注意是否发送查询sql,查询完再查询一次,不应该发送:");// 调用查询函数,从数据库中读取,发送sql

        System.out.println("数据库中数据为:" + userService.loadAllUsers().size());// 调用查询

    }

}

相应输出的打印如下:
image.png

总结:
    1、google的方式是较老的实现了,项目首页里面已经很久没有跟新过了,反而spring的方式会持续维护。
    2、spring的方式缓存有自己的更加强大的key生成方式、表达式,能够满足平时的各种需要。
    3、等等。
    参考了各种网上的资料,强烈建议看这里的资料:

    http://zyzcoder.iteye.com/blog/1897847
    http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ ;

    http://www.cnblogs.com/spring3214/archive/2013/06/09/3122900.html
    http://greemranqq.iteye.com/blog/2002473
    http://www.mincoder.com/article/2005.shtml
    http://haohaoxuexi.iteye.com/blog/2123030
三、源码下载

基于数据库的例子.zip

EhCache4Google.zip

上述许多内容已经过时和过期了,留存本篇文章仅为方便个人查看,原始文章的信息参考:

原始链接:https://www.chendd.cn/information/viewInformation/other/113.a

最后更新:2015-01-08 23:02:00

访问次数:246

评论次数:0

点赞个数:0

 点赞


 发表评论

当前回复:作者

 评论列表


留言区