一次疏忽导致的错误 – 对象拷贝了吗?

这一阵子一直在忙项目,现在终于可以静下心来总结这一段时间开发心得,新年第一篇打算讲下我犯的一次低级失误,或者可以说是疏忽,希望能给大家一些帮助 ~

最近,笔者也有一些事不顺心,人生不如意事十之八九,努力过了,该熬的夜熬过了,不后悔就够了。希望大家在新年顺顺利利 ~

问题描述

  • 场景: 业务下沉
    ,将RPC调用方的业务逻辑下沉到RPC服务方,提供通用接口。

代码对比

// 调用RPC服务获取Mission对象
ResponseDTO<Mission> res = missionService.getMissionById(missionId);
// 下沉后直接通过缓存获取对象(RPC服务原有的方法)
Mission mission = missionCache.getMissionById(missionId);
复制代码

问题代码

// set mission对象属性 (其实set很不合理,应该是一个BO)
mission.setA(A);
mission.setB(B);
// 根据mission对象属性触发业务逻辑
// ...
复制代码

根据上述改动,会发现我只是把RPC调用的逻辑下沉到了服务方,调用了服务方的方法获取了 mission
对象,但是出现了问题,很显然,导致的直接结果是 显示错乱

问题分析

想必,大家都会知道原因,如下:

1、RPC获取的对象经过反序列化,不是从数据库取出的对象,set对象属性不会影响缓存中的值。
2、缓存里取出来的是数据库取出的对象(缓存的key为missionId)是从数据库取出的对象,set对象属性会影响到其他用户的访问结果。

反序列化得到的对象为啥是新对象?

  • 将对象转换成二进制流的过程叫做 序列化
    ,将二进制流转换成对象的过程叫做 反序列化
  • 下面以示例代码为例,看下经过序列化、反序列化后的对象是否与原对象一致。
package serialization;// Java code for serialization and deserialization
// of a Java object 
import java.io.*;

class Demo implements java.io.Serializable
{
    public int a;
    public String b;

    // Default constructor 
    public Demo(int a, String b)
    {
        this.a = a;
        this.b = b;
    }

}

public class TestObject
{
    public static void main(String[] args)
    {
        Demo object = new Demo(1, "geeksforgeeks");
        String filename = "file.ser";

        // Serialization  
        try
        {
            //Saving of object in a file 
            FileOutputStream file = new FileOutputStream(filename);
            ObjectOutputStream out = new ObjectOutputStream(file);

            // Method for serialization of object 
            out.writeObject(object);

            out.close();
            file.close();

            System.out.println("Object has been serialized");
            System.out.println(object.toString());

        }

        catch(IOException ex)
        {
            System.out.println("IOException is caught");
        }


        Demo object1 = null;

        // Deserialization 
        try
        {
            // Reading the object from a file 
            FileInputStream file = new FileInputStream(filename);
            ObjectInputStream in = new ObjectInputStream(file);

            // Method for deserialization of object 
            object1 = (Demo)in.readObject();

            in.close();
            file.close();

            System.out.println("Object has been deserialized ");
            System.out.println(object1.toString());
        }

        catch(IOException ex)
        {
            System.out.println("IOException is caught");
        }

        catch(ClassNotFoundException ex)
        {
            System.out.println("ClassNotFoundException is caught");
        }

    }
} 
复制代码

运行结果

  • 可以看到,经过反序列化后,生成的对象是不一样了
Object has been serialized
serialization.Demo@23fc625e
Object has been deserialized 
serialization.Demo@1f17ae12
复制代码
  • 针对上述遇到的问题,就要使用 对象拷贝
    的方法,生成新对象进行相应业务逻辑处理。

对象拷贝

  • 对象拷贝分为 深拷贝
    浅拷贝
    ,其区别如下:

在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

最粗暴的 get/set
方法

实现 clonable
接口

class Demo implements Cloneable {
    public int a;
    public String b;

    // Default constructor 
    public Demo(int a, String b) {
        this.a = a;
        this.b = b;
    }

    @Override
    protected Object clone() {
        Demo demo = null;
        try {
            demo = (Demo) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return demo;
    }

    public static void main(String[] args) {
        Demo demo1 = new Demo(1, "1");
        Demo demo2 = (Demo) demo1.clone();
        System.out.println(demo1 == demo2);
    }
}
复制代码

使用 apache
或者 spring
的工具类, BeanUtils

1、spring (org.springframework.beans.BeanUtils) 2、apache commons-beanutils(org.apache.commons.beanutils.BeanUtils)
// org.springframework.beans.BeanUtils,
// a拷贝到b
// org.apache.commons.beanutils.BeanUtils,
// b拷贝到a
BeanUtils.copyProperties(a, b);
复制代码
  • 这个小失误,笔者最近犯了2次,想必对大家来说都是不会犯的错误,特别是在RPC业务逻辑下沉的时候,需要注意这些,因为RPC调用方拿到 反序列化后的对象set一些属性的操作(set属性不合理)
    ,如果不加思索,就会产生一些问题。

原文 

https://juejin.im/post/5e14434bf265da5d220209af

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 一次疏忽导致的错误 – 对象拷贝了吗?

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址