美文网首页
使用Ehcache要注意的对象拷贝问题(深拷贝、浅拷贝)

使用Ehcache要注意的对象拷贝问题(深拷贝、浅拷贝)

作者: yesAnd_ | 来源:发表于2020-03-15 21:22 被阅读0次

一、前言

最近工作中使用到Spring Ehcache作为一级缓存以减轻对redis的压力,在代码改造后遇到了一个对象拷贝的问题,在这里记录下踩到的坑。

将获取的文章详情使用ehcache缓存到本地内从中,访问量大的时候会先走本地缓存拿数据,然后再Redis、数据库,ehcache相关代码如下:

@Cacheable(value = EhCacheSpaceKey.ContentApi.EHCACHE_DETAIL_CONTENTVO, key = "#root.targetClass.name+#root.methodName+#id")
public ContentApiVo getContentDetail(String id) {

    //获取文章详情
    return contentApiVo;
}

测试时发现响应确实更快、redis的压力也小了,就在以为大功告成时,发现串数据的问题,看代码是如何翻车的:

public String getContentDetail(String contentId, String id, String userId, String appId, Integer contentType) {
  
   ContentApiVo contentApiVoCache = ehCacheService.getContentDetail(id);
  
  //处理数据
        
}

拿到缓存中的contentApiVoCache后,进行数据处理然后返回给前端,但是在请求量起来后会出现串数据的现象,排查后发现问题出在了contentApiVoCache这个对象上,原因在于ehcache在命中缓存后,会直接返该对象,也就是再一次缓存周期中,每次返回的都是同一个对象,后续的数据处理必然会改变数据的内容。所以在需要改变缓存结果的操作时,所以要拷贝一个新的对象进行操作。这里就引出了对象拷贝。

二、对象拷贝

1、基本概念

在Java中谈到对象拷贝,我们主要是说浅拷贝(shallow copy)深拷贝(deep copy)两种方式。

  • 浅拷贝 , 就是仅把原来对象的属性值复制到新对象对应属性上,对于基本类型属性,会对值进行复制,而引用类型属性则是直接复制应用对象的地址,这就导致复制后的新对象会依赖原对象的属性。
  • 深拷贝,基础类型属性和浅拷贝的一致,引用类型属性则是新生成一个引用,把原属性里的内容复制到新引用中,这样拷贝出来的对象,和原来的对象完全独立。

2、Cloneable接口

我们可以实现Cloneable接口进行代码拷贝,构造两个对象进行说明

Address:

public class Address implements Cloneable{

    private String city;

    private String street;

        //标准构造方法、setter、getter省略

    //重写toString方法,便于输出结果
    @Override
    public String toString() {
        return "Address{" +
                "city='" + city + '\'' +
                ", street='" + street + '\'' +
                '}';
    }
}

User:

public class User implements Cloneable{

    private String name;

    private Integer age;

    private Address address;

    @Override
    public Object clone() throws CloneNotSupportedException {
        //调用super.clone()方法进行拷贝
        return super.clone();
    }
  
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

Address作为User的一个引用类型的属性,两个对象都实现了Cloneable接口,调用super.clone()方法进行拷贝复制,编写一个测试类,使用clone()复制一个新对象,改变新对象里的引用属性值,观察是否会影响到原对象的值:

public static void main(String[] args) {

    Address address= new Address("北京","长安街");
    User user = new User("wyj",28,address);
    System.out.println("user = " + user.toString());
    User copyUser = null;
    try {
        copyUser = (User)user.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    copyUser.getAddress().setCity("上海");
    System.out.println("copyUser = " + copyUser.toString());
    System.out.println("second print user = " + user.toString());
}

输出结果:

user = User{name='wyj', age=28, address=Address{city='北京', street='长安街'}}
copyUser = User{name='wyj', age=28, address=Address{city='上海', street='长安街'}}
second print user = User{name='wyj', age=28, address=Address{city='上海', street='长安街'}}

可以看出,将新复制出来的user对象中的address属性的city值由”北京“设置为”上海“,再次输出原user的值,发现city值也变为了”上海“,即可说明,user和copuUser的值都是指向了一个Address,即Cloneable接口默认实现的是浅拷贝,那么如何实现深拷贝呢?

3、重写Clone()方法,实现深拷贝

道理很简单,我们需要重写User类的clone()方法达到深拷贝的目的,代码如下:

@Override
public Object clone() throws CloneNotSupportedException {
    User user = (User) super.clone();
    //复制一个新的address设置到user中
    user.address = (Address) this.address.clone();
    return user;
}

在clone()方法中,调用super.clone后,我们重新对得到的user的address赋新值,来避免和原user中的address值一样,注意:如果Address中也有引用类型属性,也要对其clone方法进行重写,拷贝对象有多层引用要重写多层clone()方法。再次输出结果:

user = User{name='wyj', age=28, address=Address{city='北京', street='长安街'}}
copyUser = User{name='wyj', age=28, address=Address{city='上海', street='长安街'}}
second print user = User{name='wyj', age=28, address=Address{city='北京', street='长安街'}}

可以看到,这次user和copyUser中的值互不影响,实现了深拷贝。

三、总结

深拷贝、浅拷贝,并未好坏之分,要看业务场景,深拷贝实现时会重新new出一个新的引用,所以开销也要比浅拷贝大,回到ehecache的问题上,如果仅是从缓存中国取出数据返给前端,不需要对数据进行加工,也就无所谓复制新对象了。

欢迎拍砖,欢迎交流~

注:转载请注明出处

相关文章

  • 使用Ehcache要注意的对象拷贝问题(深拷贝、浅拷贝)

    一、前言 最近工作中使用到Spring Ehcache作为一级缓存以减轻对redis的压力,在代码改造后遇到了一个...

  • PHP之剑走偏锋的DeepCopy

    php的深拷贝和浅拷贝问题,普通变量对象的赋值 = 是深拷贝,& 是浅拷贝。 输出结果: 类对象的拷贝问题就得看下...

  • java 对象的拷贝

    拷贝:即复制 对象拷贝:即对象复制 java 对象拷贝分类:浅拷贝、深拷贝 java 对象的浅拷贝和深拷贝针对包含...

  • 2018-10-10函数基础

    深拷贝和浅拷贝 深拷贝 copy.deepcopy(对象)浅拷贝 copy.copy(对象)深拷贝: 将对象对应的...

  • iOS深拷贝(MutableCopy)与浅拷贝(Copy)的区别

    深拷贝和浅拷贝的概念 iOS中有深拷贝和浅拷贝的概念,那么何为深拷贝何为浅拷贝呢?浅拷贝:浅拷贝并不拷贝对象本身,...

  • 2018-10-10day9函数基础

    1.浅拷贝、深拷贝 copy.copy(对象):浅拷贝copy.deepcopy(对象):深拷贝""" """拷贝...

  • 学习总结小知识点

    深拷贝、浅拷贝问题 对于不可变对象而言,copy便是浅拷贝,而mutableCopy是深拷贝。对于可变对象而言,c...

  • 小问题笔记

    深拷贝,浅拷贝 此问题针对于引用类型浅拷贝,拷贝(地址)完后,改变任意对象的值后,均改变深拷贝,拷贝完后,与前对象...

  • 高级二.深浅copy

    1. ==,is的使用 2.拷贝 2.1 浅拷贝与深拷贝 浅拷贝是对于一个对象...

  • 深拷贝浅拷贝

    1.拷贝可变对象 深拷贝后浅拷贝可变对象,都是内存复制。 输出结果: 2.拷贝不可变对象 浅拷贝是指针拷贝,深拷贝...

网友评论

      本文标题:使用Ehcache要注意的对象拷贝问题(深拷贝、浅拷贝)

      本文链接:https://www.haomeiwen.com/subject/orobehtx.html