• ehcache2拾遗之copyOnRead,copyOnWrite


    问题描述

    缓存在提升应用性能,提高访问效率上都是至关重要的一步。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的对象。

  • 相关阅读:
    set&enum小结(database)
    bootstrap基础
    看一篇,学一篇,今日份的pandas,你该这么学!No.2
    Python数据分析库之pandas,你该这么学!No.1
    面试Python工程师,这几道编码题有必要背背,Python面试题No8
    周三面试Python开发,这几道Python面试题差点答错,Python面试题No7
    昨天去面试,这5个Python面试题都被考到了,Python面试题No6
    2019年,Python工程师必考的6个面试题,Python面试题No5
    去面试Python工程师,这几个基础问题一定要能回答,Python面试题No4
    学习Python一年,基础忘记了,看看面试题回忆回议,Python面试题No3
  • 原文地址:https://www.cnblogs.com/resentment/p/5789644.html
Copyright © 2020-2023  润新知