浅浅的议一下Spring整合EhCache的两种方式
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如下图所示:
可以发现它里面的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了,需要手动添加,下载页面如下:
可以发现最后一个版本是2011年的了,第二种方式是后续版本的spring自己提供的对此组件的封装库,这里且称两种方式分别为google的方式和spring的方式,所有调整后的工程lib中文件为:
为了整合测试方便,上述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开始
<?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>
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("************************发送查询 sql:select * from user");
return dataList;
}
@TriggersRemove(cacheName = "system-common" , removeAll = true)
public void insertUser(){
System.out.println("************************发送新增 sql:insert into user values (?,?,?)");
dataList.add(new User(1003 , "zengxr" , "zxr"));
}
@TriggersRemove(cacheName = "system-common" , removeAll = true)
public void deleteUser(){
System.out.println("************************发送删除 sql:delete 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());// 调用查询
}
}
相应输出的打印如下:
二、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("************************发送查询 sql:select * from user");
return dataList;
}
public void insertUser() {
System.out
.println("************************发送新增 sql:insert into user values (?,?,?)");
dataList.add(new User(1003, "zengxr", "zxr"));
}
@CacheEvict(value = "system-common", allEntries = true)
public void deleteUser() {
System.out
.println("************************发送删除 sql:delete 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
Test
以上代码运行结果为:
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());// 调用查询
}
}
相应输出的打印如下:
总结:
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
三、源码下载
点赞