反射对单例的破坏

测试的单例类:

@Data
class Singleton implements Serializable{
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

测试方法:

@Test
public void test1() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Singleton instance = Singleton.getInstance();
    Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    Singleton singleton = constructor.newInstance();
    log.info("instance==singleton ==> {}", instance == singleton);
}

输出结果:

ReflexTest - instance==singleton ==> false

我们可以知道反射会破坏单例性,而且这种方式是无法避免的,那么序列化对单例的破坏呢?

序列化对单例的破坏

单例的测试类:

@Data
class Singleton implements Serializable{
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

测试方法:

@Test
public void test2() throws Exception {
    Singleton singleton = Singleton.getInstance();
    try {
        //序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("out.txt"));
        objectOutputStream.writeObject(singleton);
        objectOutputStream.close();

        // 反序列化
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("out.txt"));
        Singleton newClazz = (Singleton) objectInputStream.readObject();
        log.info("反序列化后对象:{}", newClazz);
        objectInputStream.close();
        log.info("newClazz==singleton ==> {}", newClazz == singleton);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException exception) {
        exception.printStackTrace();
    }
}

输出结果:

ReflexTest - 反序列化后对象:Singleton()
ReflexTest - newClazz==singleton ==> false

通过对 Singleton 的序列化和反序列化,生成了一个新的对象,破坏了 Singleton 的单例性。那么我们可以通过深入理解一下反序列化过程中发生了什么?ObjectInputStream 如何起作用的?

ObjectInputStream

分析一下 ObjectInputputStreamreadObject 方法执行情况到底是怎样的。

大致流程

st=>start: 开始
e=>end: 结束
readObject=>operation: readObject
readObject0=>operation: readObject0
switchType=>condition: 判断类别为对象 TC_OBJECT
readObject=>operation: readObject
op=>operation: 其它操作
readOrdinaryObject=>operation: readOrdinaryObject

st->readObject->readObject0->switchType
switchType(yes)->readOrdinaryObject->e
switchType(no)->op

核心代码分析

// readOrdinaryObject
private Object readOrdinaryObject(boolean unshared)
    throws IOException
{
    if (bin.readByte() != TC_OBJECT) {
        throw new InternalError();
    }

    // 检查引用的类实例是否允许反序列化
    ObjectStreamClass desc = readClassDesc(false);
    desc.checkDeserialize();

    // 加载类
    Class<?> cl = desc.forClass();
    if (cl == String.class || cl == Class.class
        || cl == ObjectStreamClass.class) {
        throw new InvalidClassException("invalid class descriptor");
    }

    
    Object obj;
    try {
        // 类是否可以在运行时进行初始化,可以则调用初始化,这里就会破坏单例模式
        obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {
        throw (IOException) new InvalidClassException(
            desc.forClass().getName(),
            "unable to create instance").initCause(ex);
    }

    passHandle = handles.assign(unshared ? unsharedMarker : obj);
    ClassNotFoundException resolveEx = desc.getResolveException();
    if (resolveEx != null) {
        handles.markException(passHandle, resolveEx);
    }

    // 如果类描述符实现了 Externalizable 接口,则调用 readExternalData 进行读取数据。
    if (desc.isExternalizable()) {
        readExternalData((Externalizable) obj, desc);
    } else {
        readSerialData(obj, desc);
    }

    handles.finish(passHandle);

    // 如果 hasReadResolveMethod 检查是否设置了 readResolve方法
    if (obj != null &&
        handles.lookupException(passHandle) == null &&
        desc.hasReadResolveMethod())
    {
        // 调用readResolve方法
        Object rep = desc.invokeReadResolve(obj);
        if (unshared && rep.getClass().isArray()) {
            rep = cloneArray(rep);
        }
        // 过滤替换对象
        if (rep != obj) {
            // Filter the replacement object
            if (rep != null) {
                if (rep.getClass().isArray()) {
                    filterCheck(rep.getClass(), Array.getLength(rep));
                } else {
                    filterCheck(rep.getClass(), -1);
                }
            }
            handles.setObject(passHandle, obj = rep);
        }
    }

    return obj;
}

这里简单做一个小结:

  1. 反序列化的时候,会先检查一下类型是什么?如果是Object方法,就会调用readOrdinaryObject方法,如果是其他类型,就调用对应的方法,详情可查看 readObject0 方法
  2. readOrdinaryObject 方法里面会先读取类描述,然后检查类是否允许反序列化
  3. 通过 isInstantiable 的调用检查类是否可以在运行初始化,如果可以就会进行初始化。这里通过反射破坏了单例。
  4. 如果类描述符实现了 Externalizable 接口,则调用 readExternalData 进行读取数据。
  5. 调用 hasReadResolveMethod 检查是否设置了 readResolve 方法,如果设置了则调用readResolve方法。这里防止序列化破坏

防止序列化破坏单例

修改 Singleton 类增加 readResolve 方法。

@Data
class Singleton implements Serializable {
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    // 防止序列化破坏单例
    private Object readResolve(){
        return getInstance();
    }
}

再次调用测试方法,输出结果如下:

ReflexTest - 反序列化后对象:Singleton()
ReflexTest - newClazz==singleton ==> true