mybatis-deffload

Posted siye1989

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis-deffload相关的知识,希望对你有一定的参考价值。

1. 概述

本文,我们来分享 SQL 执行的第五部分,延迟加载的功能的实现,涉及 executor/loader 包。整体类图如下:技术图片

  • 从类图,我们发现,延迟加载的功能,是通过动态代理实现的。也就是说,通过拦截指定方法,执行数据加载,从而实现延迟加载。
  • 并且,MyBatis 提供了 Cglib 和 Javassist 两种动态代理的创建方式。

在 《精尽 MyBatis 源码分析 —— SQL 执行(四)之 ResultSetHandler》 方法中,我们已经看到延迟加载相关的代码,下面让我们一处一处来看看。

另外,如果胖友并未使用过 MyBatis 的延迟加载的功能,可以先看看 《【MyBatis框架】高级映射-延迟加载》 文章。

2. ResultLoader

在 DefaultResultSetHandler 的 #getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法中,我们可以看到 ResultLoader 的身影。代码如下:

// DefaultResultSetHandler.java

// 获得嵌套查询的值
private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException
// 获得内嵌查询的编号
final String nestedQueryId = constructorMapping.getNestedQueryId();
// 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null)
// 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = constructorMapping.getJavaType();
// <x> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// 加载结果
value = resultLoader.loadResult();

return value;

  • <x> 处,因为是结果对象的构造方法中使用的值,无法使用延迟加载的功能,所以使用 ResultLoader 直接加载。

org.apache.ibatis.executor.loader.ResultLoader ,结果加载器。

2.1 构造方法

// ResultLoader.java

protected final Configuration configuration;
protected final Executor executor;
protected final MappedStatement mappedStatement;
/**
* 查询的参数对象
*/
protected final Object parameterObject;
/**
* 结果的类型
*/
protected final Class<?> targetType;
protected final ObjectFactory objectFactory;
protected final CacheKey cacheKey;
protected final BoundSql boundSql;
/**
* ResultExtractor 对象
*/
protected final ResultExtractor resultExtractor;
/**
* 创建 ResultLoader 对象时,所在的线程
*/
protected final long creatorThreadId;

/**
* 是否已经加载
*/
protected boolean loaded;
/**
* 查询的结果对象
*/
protected Object resultObject;

public ResultLoader(Configuration config, Executor executor, MappedStatement mappedStatement, Object parameterObject, Class<?> targetType, CacheKey cacheKey, BoundSql boundSql)
this.configuration = config;
this.executor = executor;
this.mappedStatement = mappedStatement;
this.parameterObject = parameterObject;
this.targetType = targetType;
this.objectFactory = configuration.getObjectFactory();
this.cacheKey = cacheKey;
this.boundSql = boundSql;
// 初始化 resultExtractor
this.resultExtractor = new ResultExtractor(configuration, objectFactory);
// 初始化 creatorThreadId
this.creatorThreadId = Thread.currentThread().getId();

  • 重点属性,看添加了中文注释的。

2.2 loadResult

#loadResult() 方法,加载结果。代码如下:

// ResultLoader.java

public Object loadResult() throws SQLException
// <1> 查询结果
List<Object> list = selectList();
// <2> 提取结果
resultObject = resultExtractor.extractObjectFromList(list, targetType);
// <3> 返回结果
return resultObject;

  • <1> 处,调用 #selectList() 方法,查询结果。详细解析,见 「2.3 selectList」 。
  • <2> 处,调用 ResultExtractor#extractObjectFromList(List<Object> list, Class<?> targetType) 方法,提取结果。详细解析,见 「3. ResultExtractor」 。
  • <3> 处,返回结果。

2.3 selectList

#selectList() 方法,查询结果。代码如下:

// ResultLoader.java

private <E> List<E> selectList() throws SQLException
// <1> 获得 Executor 对象
Executor localExecutor = executor;
if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed())
localExecutor = newExecutor();

// <2> 执行查询
try
return localExecutor.query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
finally
// <3> 关闭 Executor 对象
if (localExecutor != executor)
localExecutor.close(false);



  • <1> 处,如果当前线程不是创建线程,则调用 #newExecutor() 方法,创建 Executor 对象,因为 Executor 是非线程安全的。代码如下:

    // ResultLoader.java

    private Executor newExecutor()
    // 校验 environment
    final Environment environment = configuration.getEnvironment();
    if (environment == null)
    throw new ExecutorException("ResultLoader could not load lazily. Environment was not configured.");

    // 校验 ds
    final DataSource ds = environment.getDataSource();
    if (ds == null)
    throw new ExecutorException("ResultLoader could not load lazily. DataSource was not configured.");

    // 创建 Transaction 对象
    final TransactionFactory transactionFactory = environment.getTransactionFactory();
    final Transaction tx = transactionFactory.newTransaction(ds, null, false);
    // 创建 Executor 对象
    return configuration.newExecutor(tx, ExecutorType.SIMPLE);

  • <2> 处,调用 Executor#query(...) 方法,执行查询。

  • <3> 处,如果是新创建的 Executor 对象,则调用 Executor#close() 方法,关闭 Executor 对象。

2.4 wasNull

#wasNull() 方法,是否结果为空。代码如下:

// ResultLoader.java

public boolean wasNull()
return resultObject == null;

3. ResultExtractor

org.apache.ibatis.executor.ResultExtractor ,结果提取器。代码如下:

// ResultExtractor.java

public class ResultExtractor

private final Configuration configuration;
private final ObjectFactory objectFactory;

public ResultExtractor(Configuration configuration, ObjectFactory objectFactory)
this.configuration = configuration;
this.objectFactory = objectFactory;


/**
* 从 list 中,提取结果
*
* @param list list
* @param targetType 结果类型
* @return 结果
*/
public Object extractObjectFromList(List<Object> list, Class<?> targetType)
Object value = null;
// 情况一,targetType 就是 list ,直接返回
if (targetType != null && targetType.isAssignableFrom(list.getClass()))
value = list;
// 情况二,targetType 是集合,添加到其中
else if (targetType != null && objectFactory.isCollection(targetType))
// 创建 Collection 对象
value = objectFactory.create(targetType);
// 将结果添加到其中
MetaObject metaObject = configuration.newMetaObject(value);
metaObject.addAll(list);
// 情况三,targetType 是数组
else if (targetType != null && targetType.isArray())
// 创建 array 数组
Class<?> arrayComponentType = targetType.getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
// 赋值到 array 中
if (arrayComponentType.isPrimitive())
for (int i = 0; i < list.size(); i++)
Array.set(array, i, list.get(i));

value = array;
else
value = list.toArray((Object[]) array);

// 情况四,普通对象,取首个对象
else
if (list != null && list.size() > 1)
throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
else if (list != null && list.size() == 1)
value = list.get(0);


return value;



  • 分成四种情况,胖友看下代码注释。

4. ResultLoaderMap

在 DefaultResultSetHandler 的 #getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法中,我们可以看到 ResultLoaderMap 的身影。代码如下:

// DefaultResultSetHandler.java

// 获得嵌套查询的值
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException
// 获得内嵌查询的编号
final String nestedQueryId = propertyMapping.getNestedQueryId();
// 获得属性名
final String property = propertyMapping.getProperty();
// 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null)
// 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
// <y> 检查缓存中已存在
if (executor.isCached(nestedQuery, key))
// 创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结采对象
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
// 返回已定义
value = DEFERED;
// 检查缓存中不存在
else
// 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// <x> 如果要求延迟加载,则延迟加载
if (propertyMapping.isLazy())
// 如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象。
lazyLoader.addLoader(property, metaResultObject, resultLoader);
// 返回已定义
value = DEFERED;
// 如果不要求延迟加载,则直接执行加载对应的值
else
value = resultLoader.loadResult();



return value;

  • <x> 处,因为是结果对象的 setting 方法中使用的值,可以使用延迟加载的功能,所以使用 ResultLoaderMap 记录。最终会创建结果对象的代理对象,而 ResultLoaderMap 对象会传入其中,作为一个参数。从而能够,在加载该属性时,能够调用 ResultLoader#loadResult() 方法,加载结果。
  • 另外,在 <y> 处,检查缓存中已存在,则会调用 Executor#deferLoad(...) 方法,尝试加载结果。代码如下:

    老艿艿:此处是插入,?? 找不到适合放这块内容的地方了,哈哈哈。

    // 该方法在 BaseExecutor 抽象类中实现
    // BaseExecutor.java

    /**
    * DeferredLoad( 延迟加载 ) 队列
    */
    protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;

    @Override
    public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType)
    // 如果执行器已关闭,抛出 ExecutorException 异常
    if (closed)
    throw new ExecutorException("Executor was closed.");

    // 创建 DeferredLoad 对象
    DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
    // 如果可加载,则执行加载
    if (deferredLoad.canLoad())
    deferredLoad.load();
    // 如果不可加载,则添加到 deferredLoads 中
    else
    deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));



    private static class DeferredLoad

    private final MetaObject resultObject;
    private final String property;
    private final Class<?> targetType;
    private final CacheKey key;
    private final PerpetualCache localCache;
    private final ObjectFactory objectFactory;
    private final ResultExtractor resultExtractor;

    // issue #781
    public DeferredLoad(MetaObject resultObject,
    String property,
    CacheKey key,
    PerpetualCache localCache,
    Configuration configuration,
    Class<?> targetType)
    this.resultObject = resultObject;
    this.property = property;
    this.key = key;
    this.localCache = localCache;
    this.objectFactory = configuration.getObjectFactory();
    this.resultExtractor = new ResultExtractor(configuration, objectFactory);
    this.targetType = targetType;


    public boolean canLoad()
    return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;


    public void load()
    @SuppressWarnings("unchecked")
    // we suppose we get back a List
    // 从缓存 localCache 中获取
    List<Object> list = (List<Object>) localCache.getObject(key);
    // 解析结果
    Object value = resultExtractor.extractObjectFromList(list, targetType);
    // 设置到 resultObject 中
    resultObject.setValue(property, value);



    • 代码比较简单,胖友自己瞅瞅。

org.apache.ibatis.executor.loader.ResultLoaderMap , ResultLoader 的映射。该映射,最终创建代理对象时,会作为参数传入代理。

4.1 构造方法

// ResultLoaderMap.java

/**
* LoadPair 的映射
*/
private final Map<String, LoadPair> loaderMap = new HashMap<>();

4.2 addLoader

#addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) 方法,添加到 loaderMap 中。代码如下:

// ResultLoaderMap.java

public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader)
String upperFirst = getUppercaseFirstProperty(property);
// 已存在,则抛出 ExecutorException 异常
if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst))
throw new ExecutorException("Nested lazy loaded result property ‘" + property +
"‘ for query id ‘" + resultLoader.mappedStatement.getId() +
" already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");

// 创建 LoadPair 对象,添加到 loaderMap 中
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));


/**
* 使用 . 分隔属性,并获得首个字符串,并大写
*
* @param property 属性
* @return 字符串 + 大写
*/
private static String getUppercaseFirstProperty(String property)
String[] parts = property.split("\\.");
return parts[0].toUpperCase(Locale.ENGLISH);

  • 其中,LoadPair 是 ResultLoaderMap 的内部静态类。代码如下:

    // ResultLoaderMap.java

    public static class LoadPair implements Serializable

    private static final long serialVersionUID = 20130412;

    /**
    * Name of factory method which returns database connection.
    */
    private static final String FACTORY_METHOD = "getConfiguration";
    /**
    * Object to check whether we went through serialization..
    */
    private final transient Object serializationCheck = new Object();
    /**
    * Meta object which sets loaded properties.
    */
    private transient MetaObject metaResultObject;
    /**
    * Result loader which loads unread properties.
    */
    private transient ResultLoader resultLoader;
    /**
    * Wow, logger.
    */
    private transient Log log;
    /**
    * Factory class through which we get database connection.
    */
    private Class<?> configurationFactory;
    /**
    * Name of the unread property.
    */
    private String property;
    /**
    * ID of SQL statement which loads the property.
    */
    private String mappedStatement;
    /**
    * Parameter of the sql statement.
    */
    private Serializable mappedParameter;

    private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader)
    this.property = property;
    this.metaResultObject = metaResultObject;
    this.resultLoader = resultLoader;

    /* Save required information only if original object can be serialized. */
    // 当 `metaResultObject.originalObject` 可序列化时,则记录 mappedStatement、mappedParameter、configurationFactory 属性
    if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable)
    final Object mappedStatementParameter = resultLoader.parameterObject;

    /* @todo May the parameter be null? */
    if (mappedStatementParameter instanceof Serializable)
    this.mappedStatement = resultLoader.mappedStatement.getId();
    this.mappedParameter = (Serializable) mappedStatementParameter;

    this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
    else
    Log log = this.getLogger();
    if (log.isDebugEnabled())
    log.debug("Property [" + this.property + "] of ["
    + metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
    + "after deserialization. Make sure it‘s loaded before serializing "
    + "forenamed object.");





    // ... 暂时省略其它方法

4.3 load

#load(String property) 方法,执行指定属性的加载。代码如下:

// ResultLoaderMap.java

public boolean load(String property) throws SQLException
// 获得 LoadPair 对象,并移除
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
// 执行加载
if (pair != null)
pair.load();
return true; // 加载成功

return false; // 加载失败

  • 调用 LoadPair#load() 方法,执行加载。代码如下:
// ResultLoaderMap.java

public void load() throws SQLException
/* These field should not be null unless the loadpair was serialized.
* Yet in that case this method should not be called. */
// 若 metaResultObject 或 resultLoader 为空,抛出 IllegalArgumentException 异常
if (this.metaResultObject == null)
throw new IllegalArgumentException("metaResultObject is null");

if (this.resultLoader == null)
throw new IllegalArgumentException("resultLoader is null");


// 执行加载
this.load(null);


public void load(final Object userObject) throws SQLException
if (this.metaResultObject == null || this.resultLoader == null) // <1>
if (this.mappedParameter == null)
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");


// 获得 Configuration 对象
final Configuration config = this.getConfiguration();
// 获得 MappedStatement 对象
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null)
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");


// 获得对应的 MetaObject 对象
this.metaResultObject = config.newMetaObject(userObject);
// 创建 ResultLoader 对象
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);


/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren‘t thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
if (this.serializationCheck == null) // <2>
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);


// <3>
this.metaResultObject.setValue(property, this.resultLoader.loadResult());

  • <1> 和 <2> 处,胖友可以暂时无视,主要用于延迟加载在序列化和反序列化的时候,一般很少碰到。当然,感兴趣的胖友,可以调试下 org.apache.ibatis.submitted.lazy_deserialize.LazyDeserializeTest 单元测试类。
  • 【重点】<3> 处,调用 ResultLoader#loadResult() 方法,执行查询结果。
  • <3> 处,调用 MetaObject#setValue(String name, Object value) 方法,设置属性。

4.4 loadAll

#loadAll() 方法,执行所有属性的加载。代码如下:

// ResultLoaderMap.java

public void loadAll() throws SQLException
// 遍历 loaderMap 属性
final Set<String> methodNameSet = loaderMap.keySet();
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
for (String methodName : methodNames)
// 执行加载
load(methodName);


4.5 其它方法

ResultLoaderMap 还有其它方法,比较简单,胖友可以自己看看。

5. ProxyFactory

在 DefaultResultSetHandler 的 #createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) 方法中,我们可以看到 ProxyFactory 的身影。代码如下:

// DefaultResultSetHandler.java

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException
// useConstructorMappings ,表示是否使用构造方法创建该结果对象。此处将其重置
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>(); // 记录使用的构造方法的参数类型的数组
final List<Object> constructorArgs = new ArrayList<>(); // 记录使用的构造方法的参数值的数组
// 创建映射后的结果对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType()))
// 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings)
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy())
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); // <X>
break;



// 判断是否使用构造方法创建该结果对象
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;

  • <x> 处,调用 ProxyFactory#createProxy(...) 方法,创建结果对象的代理对象。

org.apache.ibatis.executor.loader.ProxyFactory ,代理工厂接口,用于创建需要延迟加载属性的结果对象。代码如下:

// ProxyFactory.java

public interface ProxyFactory

// 设置属性,目前是空实现。可以暂时无视该方法
void setProperties(Properties properties);

// 创建代理对象
Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);


  • ProxyFactory 有 JavassistProxyFactory 和 CglibProxyFactory 两个实现类,默认使用前者。原因见如下代码:

    // Configuration.java

    protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

5.1 JavassistProxyFactory

org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory ,实现 ProxyFactory 接口,基于 Javassist 的 ProxyFactory 实现类。

5.1.1 构造方法

// JavassistProxyFactory.java

private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);

private static final String FINALIZE_METHOD = "finalize";
private static final String WRITE_REPLACE_METHOD = "writeReplace";

public JavassistProxyFactory()
try
// 加载 javassist.util.proxy.ProxyFactory 类
Resources.classForName("javassist.util.proxy.ProxyFactory");
catch (Throwable e)
throw new IllegalStateException("Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.", e);


5.1.2 createDeserializationProxy

#createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建支持反序列化的代理对象。代码如下:

// JavassistProxyFactory.java

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);

  • 因为实际场景下,不太使用该功能,所以本文暂时无视。

5.1.3 createProxy 普通方法

#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建代理对象。代码如下:

// JavassistProxyFactory.java

@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);

5.1.4 crateProxy 静态方法

#crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 静态方法,创建代理对象。代码如下:

// JavassistProxyFactory.java

static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
// 创建 javassist ProxyFactory 对象
ProxyFactory enhancer = new ProxyFactory();
// 设置父类
enhancer.setSuperclass(type);

// 根据情况,设置接口为 WriteReplaceInterface 。和序列化相关,可以无视
try
type.getDeclaredMethod(WRITE_REPLACE_METHOD); // 如果已经存在 writeReplace 方法,则不用设置接口为 WriteReplaceInterface
// 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); // 如果不存在 writeReplace 方法,则设置接口为 WriteReplaceInterface
catch (SecurityException e)
// nothing to do here


// 创建代理对象
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try
enhanced = enhancer.create(typesArray, valuesArray);
catch (Exception e)
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);


// <x> 设置代理对象的执行器
((Proxy) enhanced).setHandler(callback);
return enhanced;

  • 常见的基于 Javassist 的 API ,创建代理对象。
  • <x> 处,设置代理对象的执行器。该执行器,就是 EnhancedResultObjectProxyImpl 对象。详细解析,见 「5.1.5 EnhancedResultObjectProxyImpl」 。

5.1.5 EnhancedResultObjectProxyImpl

EnhancedResultObjectProxyImpl ,是 JavassistProxyFactory 的内部静态类,实现 javassist.util.proxy.MethodHandler 接口,方法处理器实现类。

5.1.5.1 构造方法

// JavassistProxyFactory.java

private static class EnhancedResultObjectProxyImpl implements MethodHandler

private final Class<?> type;
private final ResultLoaderMap lazyLoader;
private final boolean aggressive;
private final Set<String> lazyLoadTriggerMethods;
private final ObjectFactory objectFactory;
private final List<Class<?>> constructorArgTypes;
private final List<Object> constructorArgs;

private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
this.type = type;
this.lazyLoader = lazyLoader;
this.aggressive = configuration.isAggressiveLazyLoading();
this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes;
this.constructorArgs = constructorArgs;


// ... 暂时省略无关方法

  • 涉及的 aggressive 和 lazyLoadTriggerMethods 属性,在 Configuration 定义如下:

    // Configuration.java

    /**
    * 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods)
    */
    protected boolean aggressiveLazyLoading;

    /**
    * 指定哪个对象的方法触发一次延迟加载。
    */
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));

5.1.5.2 createProxy

#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建代理对象,并设置方法处理器为 EnhancedResultObjectProxyImpl 对象。代码如下:

因为方法名 createProxy 一直在重复,所以这里艿艿说下调用链:

「5.1.3 createProxy 普通方法」 => 「5.1.4.2 createProxy」 => 「5.1.4 createProxy 静态方法」

// JavassistProxyFactory.java

public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
final Class<?> type = target.getClass();
// 创建 EnhancedResultObjectProxyImpl 对象
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
// 创建代理对象
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
// 将 target 的属性,复制到 enhanced 中
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;

  • 代码比较简单,胖友仔细瞅瞅,不要绕晕噢。

5.1.5.3 invoke

#invoke(Object enhanced, Method method, Method methodProxy, Object[] args) 方法,执行方法。代码如下:

// JavassistProxyFactory.java

@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable
final String methodName = method.getName();
try
synchronized (lazyLoader)
// 忽略 WRITE_REPLACE_METHOD ,和序列化相关
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
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName))
// <1.1> 加载所有延迟加载的属性
if (aggressive || lazyLoadTriggerMethods.contains(methodName))
lazyLoader.loadAll();
// <1.2> 如果调用了 setting 方法,则不在使用延迟加载
else if (PropertyNamer.isSetter(methodName))
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property); // 移除
// <1.3> 如果调用了 getting 方法,则执行延迟加载
else if (PropertyNamer.isGetter(methodName))
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property))
lazyLoader.load(property);





// <2> 继续执行原方法
return methodProxy.invoke(enhanced, args);
catch (Throwable t)
throw ExceptionUtil.unwrapThrowable(t);



  • <1.1> 处,如果满足条件,则调用 ResultLoaderMap#loadAll() 方法,加载所有延迟加载的属性。
  • <1.2> 处,如果调用了 setting 方法,则调用 ResultLoaderMap#remove(String property) 方法,不在使用延迟加载。因为,具体的值都设置了,无需在延迟加载了。
  • <1.3> 处,如果调用了 getting 方法,则调用 ResultLoaderMap#load(String property) 方法,执行指定属性的延迟加载。此处,就会去数据库中查询,并设置到对应的属性。
  • <2> 处,继续执行原方法。

5.2 CglibProxyFactory

org.apache.ibatis.executor.loader.cglib.CglibProxyFactory ,实现 ProxyFactory 接口,基于 Cglib 的 ProxyFactory 实现类。

以上是关于mybatis-deffload的主要内容,如果未能解决你的问题,请参考以下文章