目錄
- 序列化
- 反序列化
- Mybatis中的實際使用
主要知識點:
- writeObject
- writeReplace
- readObject
- readResolve
序列化與反序列化的定義
序列化就是將對象轉為字節碼的過程,反序列化則是將字節碼轉換為對象的過程
序列化
JAVA序列化
java.io.ObjectOutputStream代表對象輸出流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中。
java.io.ObjectInputStream代表對象輸入流,它的readObject()方法從一個源輸入流中讀取字節序列,再把它們反序列化為一個對象,並將其返回。
只有實現了Serializable和Externalizable接口的類的對象才能被序列化。Externalizable接口繼承自 Serializable接口,實現Externalizable接口的類完全由自身來控制序列化的行為,而僅實現Serializable接口的類可以 采用默認的序列化方式 。
通常對象序列化包括如下步驟:
1) 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
2) 通過對象輸出流的writeObject()方法寫對象。
對象反序列化的步驟如下:
1) 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
2) 通過對象輸入流的readObject()方法讀取對象。
通過測試代碼分析序列化執行過程,通過本文可以理解writeObject,writeReplace方法的執行順序,以及序列化執行的具體過程。
public class Author implements Serializable {
protected int id;
protected String username;
protected String password;
protected String email;
protected String bio;
protected Section favouriteSection;
public Author() {
this(-1, null, null, null, null, null);
}
public Author(Integer id, String username, String password, String email, String bio, Section section) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.bio = bio;
this.favouriteSection = section;
}
public Author(int id) {
this(id, null, null, null, null, null);
}
public void setId(int id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public void setBio(String bio) {
this.bio = bio;
}
public void setFavouriteSection(Section favouriteSection) {
this.favouriteSection = favouriteSection;
}
public int getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getEmail() {
return email;
}
public String getBio() {
return bio;
}
public Section getFavouriteSection() {
return favouriteSection;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
System.out.println("readObject");
in.defaultReadObject();
}
private void writeObject(ObjectOutputStream out) throws IOException{
System.out.println("writeObject");
out.defaultWriteObject();
}
Object writeReplace() throws ObjectStreamException{
System.out.println("writeReplace");
Author replaced=new Author();
replaced.setId(123);
return replaced;
}
@Test
public void testSeria() throws Exception{
Author author=new Author();
author.setId(456);
Serializable result= deserialize(serialize((Serializable)author));
System.out.println(((Author)result).getId());
}
protected byte[] serialize(Serializable value) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(value);
oos.flush();
oos.close();
return bos.toByteArray();
}
protected Serializable deserialize(byte[] value) throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(value);
ObjectInputStream ois = new ObjectInputStream(bis);
Serializable result = (Serializable) ois.readObject();
ois.close();
return result;
}
上面的測試代碼我們序列化的是ID是456的Author對象,但是反序列化后的對象確實123.
writeReplace writeObject readObject 123
這樣似乎和文章開頭介紹的序列化只是不一樣,這個是哪里除了問題呢?在上文說了通常的序列化是如上文所說處理的,但是在ObjectOuputStream中還有一個writeReplace方法,通過該方法可以替換掉原來需要序列化的對象,時序圖如下所示:
通過測試代碼可以發現,序列化過程如下:

相關源碼如下:
在調用writeObject方法時會判斷需要序列化對象是否存在writeReplace方法,如果存在則會調用該方法得到一個新的目標對象,並將其進行序列化
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
。。。。
for (;;) {
// REMIND: skip this check for strings/arrays?
Class<?> repCl;
desc = ObjectStreamClass.lookup(cl, true);
//如果存在writeReplace方法,調用需要序列化對象的writeReplace方法,如果返回對象不為null且writeReplace返回對象和原序列化對象類相同則退出循環
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
。。。
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) { //如果實現了Serializable接口
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
。。。
try {
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {//寫序列化數據
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
private void writeSerialData(Object obj, ObjectStreamClass desc)
throws IOException{
。。。
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
//調用序列化對象的writeObject方法
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
反序列化
上面主要講述了序列化的相關知識,那么反序列化呢?下面就介紹一下反序列化步驟。

主要的幾個方法如下:
-->readObject
|
\-->readObject0
|
\--->readOrdinaryObject
|
|--->invokeReadObject
| |
| \--->readObjectMethod.invoke
\------>invokeReadReslove
public final Object readObject() throws IOException, ClassNotFoundException {
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
//調用readObject0方法完成反序列化
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;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
private Object readObject0(boolean unshared) throws IOException {
。。。。
try {
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:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
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);
}
//完成對象的反序列化
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
//如果這個對象實現了readResolve方法則調用該方法
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
Mybatis中的實際使用
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
//如果當前調用是writeReplace 則表示需要對象當前對象進行序列化,而當前對象是一個代理對象,直接對當前對象序列化顯然是不合理的
//在上面的代碼可以發現代理對對象都存在writeReplace方法,在序列化的時候會調用該方法,在對該方法進行增強處理返回原始對對象,如果需要延遲加載
//則返回了一個序列化狀態保持對象
if (WRITE_REPLACE_METHOD.equals(methodName)) {
Object original;
//創建一個新的原始對象
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
//復制屬性
PropertyCopier.copyBeanProperties(type, enhanced, original);
//如果存在延遲加載對象則創建一個代理對象
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {//如果沒有延遲加載對象
return original;
}
} else {// 如果不是writeReplace方法
//如果懶加載對象大於0且當前不是finalize方法
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
//如果是積極的懶加載模式或者調用列觸發加載全部方法則加載全部懶加載屬性
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
//如果setter的屬性是懶加載的則從懶加載映射表中刪除
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
//如果getter的屬性是懶加載的此時需要加載
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
@Override
public final void writeExternal(final ObjectOutput out) throws IOException {
boolean firstRound = false;
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream os = stream.get();
if (os == null) {
os = new ObjectOutputStream(baos);
firstRound = true;
stream.set(os);
}
//序列化userBean
os.writeObject(this.userBean);
//序列化未加載屬性
os.writeObject(this.unloadedProperties);
//序列化對象工廠
os.writeObject(this.objectFactory);
//序列化構造參數類型
os.writeObject(this.constructorArgTypes);
//序列化構造參數
os.writeObject(this.constructorArgs);
final byte[] bytes = baos.toByteArray();
//完成序列化
out.writeObject(bytes);
if (firstRound) {
stream.remove();
}
}
@Override
public final void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
//反序列化
final Object data = in.readObject();
if (data.getClass().isArray()) {
this.userBeanBytes = (byte[]) data;
} else {
this.userBean = data;
}
}
@SuppressWarnings("unchecked")
protected final Object readResolve() throws ObjectStreamException {
//如果userBean不為null且userBeanBytes長度為0則表示已經完成過 直接返回該對象
if (this.userBean != null && this.userBeanBytes.length == 0) {
return this.userBean;
}
/*第一次 */
try {
final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(this.userBeanBytes));
//反序列化userBean
this.userBean = in.readObject();
//反序列化延遲加載屬性
this.unloadedProperties = (Map<String, ResultLoaderMap.LoadPair>) in.readObject();
//反序列化對象工廠
this.objectFactory = (ObjectFactory) in.readObject();
//反序列化構造參數類型
this.constructorArgTypes = (Class<?>[]) in.readObject();
//反序列化構造參數
this.constructorArgs = (Object[]) in.readObject();
} catch (final IOException ex) {
throw (ObjectStreamException) new StreamCorruptedException().initCause(ex);
} catch (final ClassNotFoundException ex) {
throw (ObjectStreamException) new InvalidClassException(ex.getLocalizedMessage()).initCause(ex);
}
final Map<String, ResultLoaderMap.LoadPair> arrayProps = new HashMap<String, ResultLoaderMap.LoadPair>(this.unloadedProperties);
final List<Class<?>> arrayTypes = Arrays.asList(this.constructorArgTypes);
final List<Object> arrayValues = Arrays.asList(this.constructorArgs);
//根據序列化是保存的數據創建一個反序列化代理對象
return this.createDeserializationProxy(userBean, arrayProps, objectFactory, arrayTypes, arrayValues);
}
