前面我们分析了mybatis的懒加载是通过ProxyFactory生成代理对象以AOP的方式实现的。通过拦截对象的方法,如果发现方法与懒加载的属性相关时才能属性进行加载然后执行相应的方法。但是,这个时候有一个序列化的问题。如果我们对这个懒加载涉及的对象进行了序列化与反序列化,那么反序列化得到的对象属性的正确性吗?
如果序列化时对象的所有属性都已经被加载,此时序列化自然是不会对对象造成影响的。那么按照这个思路,我们可以在对对象序列化时加载所有还没有加载的属性以保证正确性,但是这样子无法满足不符合懒加载的按需加载的特性。
下面,我们将继续了解懒加载的源码,看看mybatis是如何处理这个问题的。
一、代理对象的序列化
writeReplace方法是一个序列化的相关方法,可以在序列化是实现对象的替换。
1. 保证代理对象实现了writeReplace()方法
无论是CglibProxyFactory还是JavassistProxyFactory中,在生成代理对象时,都保证了对象含有writeReplace()方法。
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (log.isDebugEnabled()) {
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// nothing to do here
}
可以看到,如果没有writeReplace方法,则要求其实现WriteReplaceInterface接口,保证了对象含有writeReplace()方法。
public interface WriteReplaceInterface {
Object writeReplace() throws ObjectStreamException;
}
2. writeReplace()方法中自定义序列化逻辑
既然保证了对象含有writeReplace()方法,那么对象的序列化必然是按照writeReplace()的方法逻辑进行,接下来看writeReplace()方法的逻辑,这一点在CglibProxyFactory和JavassistProxyFactory中还是相同的。
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 CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
}
可以看到,前面两步是通过构造方法进行原对象的初始化和属性复制,最后一步按照懒加载状态进行判断:
(1)没有加载完成,用CglibSerialStateHolder对象进行替换。(JavassistProxyFactory则是JavassistSerialStateHolder对象)
(2)加载完成,直接返回原对象进行替换。
果序列化时对象的所有属性都已经被加载,序列化时用原对象进行替换,反序列化后得到原来的对象,不会对象的正确性造成任何影响。我们重点关注的是CglibSerialStateHolder。
二、AbstractSerialStateHolder
AbstractSerialStateHolder是CglibProxyFactory和JavassistProxyFactory的公共父类,由于代理对象通过writeObject方法将其序列化替换成了CglibProxyFactory或者JavassistProxyFactory的序列化,而AbstractSerialStateHolder实现了Externalizable接口。
1. 序列化
@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);
}
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();
}
}
可以看到,序列化是将将对象的属性转化成字节数组。
2. 反序列化
@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 {
/* Second run */
if (this.userBean != null && this.userBeanBytes.length == 0) {
return this.userBean;
}
/* First run */
try {
final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(this.userBeanBytes));
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);
}
这里同样通过readResolve()实现了反序列化的对象替换,替换成createDeserializationProxy方法的返回值。
三、CglibSerialStateHolder
1. CglibSerialStateHolder的createDeserializationProxy方法
在CglibSerialStateHolder中
//CglibSerialStateHolder
@Override
protected Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return new CglibProxyFactory().createDeserializationProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
2. CglibProxyFactory的createDeserializationProxy方法
//CglibProxyFactory
public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
3. CglibProxyFactory$EnhancedDeserializationProxyImpl
CglibProxyFactory$EnhancedDeserializationProxyImpl继承了AbstractEnhancedDeserializationProxy,实现了MethodInterceptor接口。
public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
可以看到,同样基于cglib生成了一个代理对象。
@Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
final Object o = super.invoke(enhanced, method, args);
return (o instanceof AbstractSerialStateHolder) ? o : methodProxy.invokeSuper(o, args);
}
public final Object invoke(Object enhanced, Method method, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
final Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
return this.newSerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
} else {
synchronized (this.reloadingPropertyLock) {
if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName) && !reloadingProperty) {
final String property = PropertyNamer.methodToProperty(methodName);
final String propertyKey = property.toUpperCase(Locale.ENGLISH);
if (unloadedProperties.containsKey(propertyKey)) {
final ResultLoaderMap.LoadPair loadPair = unloadedProperties.remove(propertyKey);
if (loadPair != null) {
try {
reloadingProperty = true;
loadPair.load(enhanced);
} finally {
reloadingProperty = false;
}
} else {
/* I'm not sure if this case can really happen or is just in tests -
* we have an unread property but no loadPair to load it. */
throw new ExecutorException("An attempt has been made to read a not loaded lazy property '"
+ property + "' of a disconnected object");
}
}
}
return enhanced;
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
可以看到,还是通过aop拦截的方式对没有加载的懒加载属性进行按需加载。
四、JavassistSerialStateHolder
JavassistSerialStateHolder的代码逻辑与CglibSerialStateHolder基本一致,只不过CglibSerialStateHolder基于Cglib生成代理对象,JavassistSerialStateHolder基于Javassist生成代理对象,不再赘述。