Mybatis延迟加载对象的序列化反序列化处理

阿凡达2018-07-13 09:34

    我们知道在Mybatis中支持延迟加载,什么是懒加载呢? 顾名思义就是在需要的时候再加载数据,而不是一开始就准备好数据。那么是如何实现延迟加载的呢?在DefaultResultSetHandler类中有这样一段代码,就是通过创建一个代理对象实现了懒加载,在调用相关方法的时候进行加载数据。

 //如果属性映射存在嵌套查询ID且配置了懒加载
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
          //创建代理对象
          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          break;
        }
目前默认的ProxyFactory是 JavassistProxyFactory 所以本次代码也是分析JavassistProxyFactory的源码
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List
这样就完成了代理对象的创建了,目前创建的对象是一个代理对象。我们知道是通过代理对象实现懒加载的,如果对当前对象进行序列化和反序列化后还可以实现懒加载吗?那么又该如何实现呢? 在说这个问题之前首先说一下序列化和反序列化的相关知识,下面看一个例子
public class Author implements Serializable {
 
  protected int id;

 
  public void setId(int id) {
    this.id = id;
  }
 
 
  public int getId() {
    return id;
  }
 
  
  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吗?实际的输出结果是:

writeReplace
writeObject
readObject
123
主要的调用过程如下,最终序列化的对象是目标对象writeReplace方法的返回值
   --> writeObject
     |
     |--->writeObject0
        |
        |--->writeOrdinaryObject
            |
            |---->invokeWriteReplace
                |
                |--->writeReplaceMethod.invoke   
那么反序列化呢?在上面的例子中增加一个方法
  Object readResolve(){
    Author author=new Author();
    author.setId(987);
    System.out.println("readResolve");
    return author;
  }
此时的输出结果是:
writeReplace
writeObject
readObject
readResolve
987
这个是为什么呢?还是通过一下简单的时序图看一下
-->readObject
    |
    \-->readObject0
       |
       \--->readOrdinaryObject
          |
          |--->invokeReadObject
          |   |
          |   \--->readObjectMethod.invoke
          |
          \--->invokeReadResolve
              |
              \---->readResolve.invoke
最终反序列化的结果是反序列化目标对象中的readResolve方法的返回值。  再介绍完序列化与反序列化以后再回过头来看 Mybatis是如何保证序列化和反序列化后的对象还是支持延迟加载的呢?
EnhancedResultObjectProxyImpl类中有如下代码,在该方法中实现了延迟加载,和序列化处理,如果有延迟加载属性,在调用writeRepalce方法返回的不是代理对象也不是原始对象,而是一个状态保持的对象。
 @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);
      }
    }
  }
class JavassistSerialStateHolder extends AbstractSerialStateHolder {

  private static final long serialVersionUID = 8940388717901644661L;

  public JavassistSerialStateHolder() {
  }

  public JavassistSerialStateHolder(
          final Object userBean,
          final Map unloadedProperties,
          final ObjectFactory objectFactory,
          List> constructorArgTypes,
          List
@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) 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 arrayProps = new HashMap(this.unloadedProperties);
    final List> arrayTypes = Arrays.asList(this.constructorArgTypes);
final List<Object> arrayValues = Arrays.asList(this.constructorArgs);
//根据序列化是保存的数据创建一个反序列化代理对象
return this.createDeserializationProxy(userBean, arrayProps, objectFactory, arrayTypes, arrayValues);
此时可以发现反序列化后的结果还是一个代理对象

相关阅读:

Mybatis BatchExecutor源码分析

Mybatis插件的使用以及源码分析

本文来自网易实践者社区,经作者张伟授权发布。