反射对单例的破坏
测试的单例类:
@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
分析一下 ObjectInputputStream 的 readObject 方法执行情况到底是怎样的。
大致流程
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;
}
这里简单做一个小结:
- 反序列化的时候,会先检查一下类型是什么?如果是Object方法,就会调用readOrdinaryObject方法,如果是其他类型,就调用对应的方法,详情可查看
readObject0方法 readOrdinaryObject方法里面会先读取类描述,然后检查类是否允许反序列化- 通过 isInstantiable 的调用检查类是否可以在运行初始化,如果可以就会进行初始化。这里通过反射破坏了单例。
- 如果类描述符实现了 Externalizable 接口,则调用 readExternalData 进行读取数据。
- 调用 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