问题描述

缓存在提升应用性能,提高访问效率上都是至关重要的一步。ehcache也是广为使用的缓存之一。但是如果将一个可变的对象(如普通的POJO/List/Map等)存入缓存中,会导致怎样潜在的问题。下面来看一个例子

    //首先创建一个简单的POJO
public class Student {
private String name;
public Student(String name){
this.setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

之后创建一个POJO并使用ehcache来进行缓存

    @Test
public void copyOnRead(){
CacheManager cache=CacheManager.create("cache.xml"); //读取cache配置文件,如果不配置ehcache有默认的failsafe配置
Ehcache copyCache=cache.addCacheIfAbsent("copyCache"); //读取名为copyCache的cache,如果没有则创建
Student stu=new Student("张三"); //创建一个简单的POJO
Element el=new Element(1,stu); //将对象放入Element对象
copyCache.put(el); //存入缓存
stu.setName("李四"); //之后改变POJO的内容
Student cachedStu=(Student) copyCache.get(1).getObjectValue();//从缓存中取出对象
System.out.println("cache中取出的对象的名字:"+cachedStu.getName());
System.out.println("cache中取出的对象和源对象相等:"+(copyCache.get(1)==el));
System.out.println("两次cache中取出的对象相等"+(copyCache.get(1)==copyCache.get(1)));
}

以上代码的输出结果是

    cache中取出的对象的名字:李四
cache中取出的对象和源对象相等:true
两次cache中取出的对象相等true

可以看到缓存中的对象在外部对象被修改时也被修改了(这里只考虑内存缓存,不考虑序列化到磁盘)。

原因分析

这是由于ehcache中存放的Element对象是直接保存了原对象的引用,所以引用的内容被修改之后cache内部的值也会被修改。

解决办法

首先这种现象并非一定是有问题的,有的应用场景就是希望保存对象的引用,但是这会导致部分误操作,如果不小心无意修改了对象的值就会导致数据不准确。

知道了原因那么也就好解决问题了:

使用immutable对象

将POJO设计成immutable的,如果需要修改就新建一个新的对象,这个也是fp所推崇的方案。

使用ehcache的copyStrategy

如果是遗留项目的话上面的方法可能就不适合了,好在在ehcache中已经实现了接口可以拦截read、write等操作,并对存入、读取的Element对象进行修改

    public class MyCopyStrategy implements ReadWriteCopyStrategy<Element> {
//当write缓存的时候会调用这个方法
public Element copyForWrite(Element value, ClassLoader loader) {
Student temp=(Student) value.getObjectValue();
return new Element(value.getObjectKey(),new Student(temp.getName()+"_w"));
}
//当read缓存的时候会调用这个方法
public Element copyForRead(Element storedValue, ClassLoader loader) {
Student temp=(Student) storedValue.getObjectValue();
return new Element(storedValue.getObjectKey(),new Student(temp.getName()+"_r"));
}
}

如上只要实现了ReadWriteCopyStrategy就可以修改存入、读取的对象。这里在读取时我再名字后加了一个r,在存入时名字后加了一个w

还需要注入到cache中

    //配置文件
<cache name="copyCache" maxEntriesLocalHeap="1" eternal="false"
timeToIdleSeconds="1" timeToLiveSeconds="1" copyOnRead="true" copyOnWrite="true">
<copyStrategy class="echach2.MyCopyStrategy" />
</cache>

使用copyStrategy将strategy配置到cache中,再运行上面的代码

    张三_w_r
cache中取出的对象和源对象相等:false
两次cache中取出的对象相等false

这里可以看到存在的cache中的对象和原对象并不一样,两次cache取出来的对象也不同。

小结

ehcache的copyStratgy可以有效防止引用对象在外部被误改,但是这每次都复制一个对象,对性能还是有一定的影响的,所以可能的话还是尽量使用immutable的对象。

ehcache2拾遗之copyOnRead,copyOnWrite的更多相关文章

  1. ehcache2拾遗之write和load

    问题描述 在cache系统中writeThrough和writeBehind是两个常用的模式. writeThrough是指,当用户更新缓存时,自动将值写入到数据源. writeBehind是指,在用 ...

  2. ehcache2拾遗之cache持久化

    问题描述 应用在使用过程中会需要重启等,但是如果ehcache随着应用一起重启,那么刚重启的时候就会出现大量的miss,需要一定的访问量来重建缓存,如果缓存能够持久化,重启之后可以复用将会有助于缓解重 ...

  3. Ehcache(2.9.x) - API Developer Guide, Transaction Support

    About Transaction Support Transactions are supported in versions of Ehcache 2.0 and higher. The 2.3. ...

  4. EhCache 配置信息

    How to Size Caches 官方文档:http://ehcache.org/documentation/configuration/cache-size [maxEntriesLocalHe ...

  5. Terracotta

    Terracotta 3.2.1简介 (一) 博客分类: 企业应用面临的问题 Java&Socket 开源组件的应用 hibernatejava集群服务器EhcacheQuartzTerrac ...

  6. Redis命令拾遗二(散列类型)

    本文版权归博客园和作者吴双共同所有,欢迎转载,转载和爬虫请注明原文地址 :博客园蜗牛NoSql系列地址  http://www.cnblogs.com/tdws/tag/NoSql/ Redis命令拾 ...

  7. 基础拾遗------redis详解

    基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...

  8. unixLike命令拾遗

    针对在日常工作过程中,发现的学习的漏洞和忘记的知识,进行拾遗. 编辑命令 一.vim操作 1.进入编辑模式 在光标移到将要编辑处,点击i,进入编辑模式 2.退出编辑模式 按esc或者crtl+c退出编 ...

  9. 基础拾遗------webservice详解

    基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...

随机推荐

  1. 菜单的隐藏&显示

    //还是先上万一老师的原代码 //隐藏与显示菜单Self.Menu := nil; {隐藏菜单} Self.Menu := MainMenu1; {显示菜单} //初一看到代码,我有点抓不到感觉的意思 ...

  2. J2EE中EL表达式

    EL全名为Expression Language. EL语法很简单,主要的语法结构是${sessionScope.user.sex}所有EL都是以${为起始.以}为结尾的. 上述EL范例的意思是:从S ...

  3. Oracle优化的方法

    想做到数据库优化的高手,不是花几周,几个月就能达到的,这并不是因为数据库优化有多高深,而是因为要做好优化一方面需要有非常好的技术功底,对操作系统.存储硬件网络.数据库原理等方面有比较扎实的基础知识,另 ...

  4. python基础第三天(1)

    函数 函数分为:内置函数,自定义函数,导入函数. 内置函数 python为咱们提供的快捷方式 vars()---针对脚本的,找到这个脚本中的所有变量. #!/usr/bin/env python # ...

  5. linux mysql操作命令

    1.linux下启动mysql的命令:mysqladmin start/ect/init.d/mysql start (前面为mysql的安装路径) 2.linux下重启mysql的命令:mysqla ...

  6. PHP二维数组排序

    $arrays   要排序的数组 $sort_key 根据排序的key $sort_order 升序降序    SORT_ASC/SORT_DESC $sort_type 排序key类型 SORT_N ...

  7. eclipse快速移动项目到另一台机器_步骤

    ​快速移动项目到另一台机器_步骤 1.设置好eclipse的编码,必须对应个人项目文件的编码{ window--preferences--general--workspace-text file en ...

  8. Java学习笔记 02 String类、StringBuilder类、字符串格式化和正则表达式

    一.String类一般字符串 声明字符串 >>String str 创建字符串 >>String(char a[])方法用于将一个字符数组创建为String对象 >> ...

  9. Python 学习---------Day1

    第一章 问答环节一.人们为何使用Python 软件质量 开发者的效率 程序的可移植性 标准库的支持 组件集成 享受乐趣二.Python的缺点 Python唯一的缺点就是:与C/C++这类编译语言相比, ...

  10. 利用yum下载软件包的三种方法

    转自:http://297020555.blog.51cto.com/1396304/530703 方法一: downloadonly插件 有一个yum的插件叫做downloadonly,顾名思义,就 ...