code-learning/mybatis/25-mybatis-SQL 执行(五)之延迟加载.md

38 KiB
Raw Blame History

精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载

1. 概述

本文,我们来分享 SQL 执行的第五部分,延迟加载的功能的实现,涉及 executor/loader 包。整体类图如下:![类图](25-mybatis-SQL 执行(五)之延迟加载.assets/01.png)类图

  • 从类图,我们发现,延迟加载的功能,是通过动态代理实现的。也就是说,通过拦截指定方法,执行数据加载,从而实现延迟加载。
  • 并且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;
    }
    
    // ... 暂时省略无关方法
}
  • 涉及的 aggressivelazyLoadTriggerMethods 属性,在 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 实现类。

CglibProxyFactory 和 JavassistProxyFactory 的代码实现非常类似,此处就不重复解析了。感兴趣的胖友,自己研究下。

666. 彩蛋

哎哟,比想象中的简单好多。嘿嘿。

参考和推荐如下文章: