首先我們來看一下序列化和反序列化是怎么破壞單例的??创a
public class HungrySingleton implements Serializable{
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
private HungrySingleton(){
if(hungrySingleton != null){
throw new RuntimeException("單例構(gòu)造器禁止反射調(diào)用");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
這里我們使用之前的餓漢式的單例作為例子。在之前餓漢式的代碼上做點(diǎn)小改動(dòng)。就是讓我們的單例類實(shí)現(xiàn) Serializable接口。然后我們?cè)跍y(cè)試類中測(cè)試一下怎么破壞。
public class SingletonTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungrySingleton instance = HungrySingleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
HungrySingleton newInstance = (HungrySingleton) ois.readObject();
System.out.println(instance == newInstance;
}
}
這里首先我們使用正常的方式來獲取一個(gè)對(duì)象。通過序列化將對(duì)象寫入文件中,然后我們通過反序列化的到一個(gè)對(duì)象,我們?cè)賹?duì)比這個(gè)對(duì)象,輸出的內(nèi)存地址和布爾結(jié)果都表示這不是同一個(gè)對(duì)象。也就說我們通過使用序列化和反序列化破壞了這個(gè)單例,那我們?cè)撊绾畏乐文兀糠乐纹饋砗芎?jiǎn)單,只需要在單例類中添加一個(gè)readResolve方法,下面看代碼:
public class HungrySingleton implements Serializable,Cloneable{
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
private Object readResolve(){
return hungrySingleton;
}
}
此時(shí)我們?cè)偻ㄟ^測(cè)試類進(jìn)行測(cè)試即可發(fā)現(xiàn)我們通過序列化和反序列化得到的還是同一個(gè)對(duì)象。那么為什么添加一個(gè)這個(gè)方法就可以防止呢?下我們跟進(jìn)去看看為什么
首先這個(gè)readResolve方法不是object里面的方法。我們進(jìn)我們的測(cè)試類中去看看這行中的HungrySingleton newInstance = (HungrySingleton) ois.readObject()中的 readObject()的實(shí)現(xiàn)。我們只把關(guān)鍵代碼貼出來。
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
我們重點(diǎn)來看一下 Object obj = readObject0(false)這一行這里調(diào)用了一個(gè)readObject0方法,我們?cè)偕钊肟匆幌逻@個(gè)readObject0方法的實(shí)現(xiàn)。
/**
* Underlying readObject implementation.
*/
private Object readObject0(boolean unshared) throws IOException {
....
//各種判斷邏輯我們暫時(shí)不管
switch (tc) {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
}
....
}
我們看這個(gè) case TC_OBJECT: 也就是判斷為object之后的代碼,checkResolve(readOrdinaryObject(unshared))這行先是調(diào)用了readOrdinaryObject()方法,然后將方法的返回值返回給checkResolve方法,我們先查看一下readOrdinaryObject()方法。
/**
* Reads and returns "ordinary" (i.e., not a String, Class,
* ObjectStreamClass, array, or enum constant) object, or null if object's
* class is unresolvable (in which case a ClassNotFoundException will be
* associated with object's handle). Sets passHandle to object's assigned
* handle.
*/
private Object readOrdinaryObject(boolean unshared){
.....
//各種判斷校驗(yàn)
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);
}
.....
return obj;
}
我們看一下 obj = desc.isInstantiable() ? desc.newInstance() : null這一行中的obj對(duì)象是干嘛用的 我們往下翻在這個(gè)方法的最后將這個(gè)obj返回出去了。我們又回頭看這個(gè)這一行obj = desc.isInstantiable() ? desc.newInstance() : null 這個(gè)進(jìn)行判斷如果 obj==desc.isInstantiable()就返回一個(gè)新的對(duì)象,否則返回空,代碼看到這里好像有點(diǎn)眉目,我再看看isInstantiable這個(gè)方法的實(shí)現(xiàn)。
/**
* Returns true if represented class is serializable/externalizable and can
* be instantiated by the serialization runtime--i.e., if it is
* externalizable and defines a public no-arg constructor, or if it is
* non-externalizable and its first non-serializable superclass defines an
* accessible no-arg constructor. Otherwise, returns false.
*/
boolean isInstantiable() {
requireInitialized();
return (cons != null);
}
isInstantiable方法實(shí)現(xiàn)很簡(jiǎn)單,這里的cons是什么呢?我們繼續(xù)看
/** serialization-appropriate constructor, or null if none */
private Constructor<?> cons;
cons是構(gòu)造器這里是通過反射獲取的對(duì)象,光看著一行代碼我們好像并不能看出啥東西,這時(shí)候我們看一下這一行代碼的注釋。 翻譯過來的話就是:
如果表示的類是serializable/externalizable并且可以由序列化運(yùn)行時(shí)實(shí)例化,則返回true - 如果它是可外部化的并且定義了公共的無參數(shù)構(gòu)造函數(shù),或者它是不可外化的,并且它的第一個(gè)非可序列化的超類定義了可訪問的無參數(shù)構(gòu)造函數(shù)。否則,返回false。
externalizable這個(gè)類是serializable的一個(gè)子類用于制定序列化,比如自定義某個(gè)屬性的序列化,用的比較少。
好,我們的單例實(shí)現(xiàn)了serializable接口所以這里返回的是true,那么回到我們之前看看到的那里,也就是這里obj = desc.isInstantiable() ? desc.newInstance() : null 此時(shí)返回的就是一個(gè)newInstance是通過反射拿到的對(duì)象,既然是反射拿到的對(duì)象自然是一個(gè)新的對(duì)象,看到這里我們算弄明白了為什么序列化獲取的是一個(gè)新的對(duì)象。不過到這里還是沒有得到我們想要的知道的為什么寫了一個(gè)readResolve方法就可以解決反序列化得到的不是同一個(gè)對(duì)象的問題,那么我們繼續(xù)往下看ObjectInputSteam這個(gè)類
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
看到這里,這里對(duì)obj進(jìn)行了一次空判斷,這里我們剛分析了obj不會(huì)為空,看這里desc.hasReadResolveMethod()從命名我們可以看出這個(gè)判斷是判斷否包含readResolve這個(gè)方法。我們?cè)冱c(diǎn)進(jìn)去看看這個(gè)的實(shí)現(xiàn)
/**
* Returns true if represented class is serializable or externalizable and
* defines a conformant readResolve method. Otherwise, returns false.
*/
boolean hasReadResolveMethod() {
requireInitialized();
return (readResolveMethod != null);
}
這里依舊是看代碼沒啥看的,我們看看注釋,符合我們的猜測(cè),也就是說這個(gè)
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
判斷結(jié)果為true那么我們?cè)倏纯催@個(gè)desc.invokeReadResolve(obj)的實(shí)現(xiàn)
/**
* Invokes the readResolve method of the represented serializable class and
* returns the result. Throws UnsupportedOperationException if this class
* descriptor is not associated with a class, or if the class is
* non-serializable or does not define readResolve.
*/
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
requireInitialized();
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(th); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
這里我們看方法名的也能猜測(cè)這是使用了反射來調(diào)用,看這一行 return readResolveMethod.invoke(obj, (Object[]) null) 使用了反射來調(diào)用readResolveMethod方法??墒悄憧赡軙?huì)問了 也沒看到用readResolveMethod這個(gè)方法啊,我對(duì)這個(gè)類進(jìn)行搜索一下 readResolve
/**
* Creates local class descriptor representing given class.
*/
private ObjectStreamClass(final Class<?> cl) {
.....
domains = getProtectionDomains(cons, cl);
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
....
在這里可以看到是獲取了readResolve這個(gè)方法。這樣就算解決了我們最初的疑問了。同學(xué)們可以根據(jù)我說的源碼在相應(yīng)的地方打斷點(diǎn)看看。