神奇的Serializable接口,为什么有时候网络传输不用实现Serializable,有时候又需要?

3,491 阅读4分钟

大家好,这里是小奏,觉得文章不错可以关注公众号小奏技术

背景

其他大家在初学java的时候肯定是接触过Serializable接口的,这个接口是一个标记接口,没有任何方法,只是一个标记,用来标记一个类可以被序列化,可以被网络传输,可以被持久化。

public interface Serializable {
}

一些传统的草台班子的解释就是,你要网络传输就要实现Serializable接口,但是这个解释并不准确

因为有时候我们发现不实现Serializable接口进行网络传输也不会报错,但是有时候又需要实现Serializable接口,否则会报错,那么底什么时候需要实现Serializable接口呢?

比如我们使用spring boot框架在controller层返回一个json数据,这个时候我们并不需要实现Serializable接口

比如我们使用redisson的发布订阅功能传输的对象又需要实现Serializable接口(使用默认的序列化MarshallingCodec)

如果是抱着能用就行的态度也是很简单,一把梭,只要网络传输就实现Serializable接口

如果想深入了解我们就可以继续向下看下去

什么情况下的网络传输需要实现Serializable接口

大家都知道Serializable接口是java原生提供的接口,那么就说明如果是使用java原生的网络传输方式就需要实现Serializable接口

使用java原生的网络传输对象就需要实现Serializable,如果只是基本类型或者字符串则不需要

这里我们以RMI为例 ,使用RMI传输java对象的的时候就需要需要实现Serializable接口

RMI传输demo

这里我们简单基于RMI传输一个对象,来看看效果,演示下不实现Serializable接口的情况

首先我们实现一个远程接口RemoteInterface

  • RemoteInterface
public interface RemoteInterface extends Remote {
    
    String sayHello(XiaoZou xiaoZou) throws RemoteException;
}

之后提供一个远程接口调用的实现类

  • RemoteImpl
public class RemoteImpl extends UnicastRemoteObject implements RemoteInterface {
    
    protected RemoteImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello(XiaoZou xiaoZou) throws RemoteException {
        return "Hello, " + xiaoZou.getName() + "!";
    }
}

注意这里我们传输的对象是XiaoZou

  • XiaoZou
public class XiaoZou implements Serializable {
    
    private String name;
    
    private String age;

    public XiaoZou() {
    }

    public XiaoZou(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

这就是我们要传输的对象,这个对象我们会进行演示实现Serializable接口和不实现Serializable接口的区别

创建一个RMI服务端

  • XiaoZouServer
public class XiaoZouServer {

    public static void main(String[] args) {
        try {
            RemoteInterface stub = new RemoteImpl();
            Registry registry = LocateRegistry.createRegistry(1099);
            registry.rebind("RemoteInterface", stub);
            System.out.println("Server is ready");
        } catch (Exception e) {
            System.err.println("Server exception: " + e);
            e.printStackTrace();
        }
    }
}

创建一个RMI客户端

public class XiaoZouClient {

    public static void main(String[] args) {
        try {
            Registry registry = LocateRegistry.getRegistry("localhost", 1099);
            RemoteInterface stub = (RemoteInterface) registry.lookup("RemoteInterface");
            String response = stub.sayHello(new XiaoZou("小奏", "18岁"));
            System.out.println("Response from server: " + response);
        } catch (Exception e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

结果

  • 正常实现Serializable接口的情况

client运行结果: Response from server: Hello, 小奏!

  • 不实现Serializable接口的情况

可以看到直接报错java.io.NotSerializableException

为什么spring boot网络传输对象不用实现Serializable接口

到这里我们已经非常清楚的知道了如果使用java原生的序列化方式就需要实现Serializable接口,然后spring boot中编写接口默认使用的是json序列化方式,所以不需要实现Serializable接口

为什么dubbo默认不是java原生序列化方式也需要实现Serializable接口

dubbo 3.x默认使用的序列化方式是hessian2Hessian不是java原生的,为什么也要实现Serializable接口呢?否者就会报错

cn.dubbo.apache.org/zh-cn/overv…

如果默认我们不实现Serializable 就会报错

ause: org.apache.dubbo.common.serialize.SerializationException: com.alibaba.fastjson2.JSONException: not support none serializable class com.xiaozoudubbo.dto.XiaoZou

原因很简单,因为dubbo存在Serializable接口检查机制

cn.dubbo.apache.org/zh-cn/overv…

Serializable接口检查模式分为两个级别:true 开启,false 关闭。开启检查后会拒绝反序列化所有未实现 Serializable 的类。

Dubbo 中默认配置为true开启检查。

所以如果我们想要在dubbo不用实现Serializable接口,我们可以关闭dubboSerializable接口检查机制

spring boot中我们仅仅需要添加配置dubbo.application.check-serializable=true即可

总结

如果使用java原生的网络传输方式(原生序列化方式)传输java对象就需要实现Serializable接口

其他的更现代化的序列化方式一般都不需要实现Serializable接口,比如jsonprotobufhessian2等等

我们使用dubbo的时候默认是需要实现Serializable接口的,因为dubbo存在Serializable接口检查机制,可以通过关闭dubboSerializable接口检查机制来解决这个问题