# 精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载 # 1. 概述 本文,我们来分享 SQL 执行的第五部分,延迟加载的功能的实现,涉及 `executor/loader` 包。整体类图如下:[![类图](25-mybatis-SQL 执行(五)之延迟加载.assets/01.png)](http://static.iocoder.cn/images/MyBatis/2020_03_12/01.png)类图 - 从类图,我们发现,延迟加载的功能,是通过**动态代理**实现的。也就是说,通过拦截指定方法,执行数据加载,从而实现延迟加载。 - 并且,MyBatis 提供了 Cglib 和 Javassist 两种动态代理的创建方式。 在 [《精尽 MyBatis 源码分析 —— SQL 执行(四)之 ResultSetHandler》](http://svip.iocoder.cn/MyBatis/executor-4) 方法中,我们已经看到延迟加载相关的代码,下面让我们一处一处来看看。 另外,如果胖友并未使用过 MyBatis 的延迟加载的功能,可以先看看 [《【MyBatis框架】高级映射-延迟加载》](https://blog.csdn.net/acmman/article/details/46696167) 文章。 # 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(); // 创建 ResultLoader 对象 final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql); // 加载结果 value = resultLoader.loadResult(); } return value; } ``` - `` 处,因为是结果对象的构造方法中使用的值,无法使用延迟加载的功能,所以使用 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 list = selectList(); // <2> 提取结果 resultObject = resultExtractor.extractObjectFromList(list, targetType); // <3> 返回结果 return resultObject; } ``` - `<1>` 处,调用 `#selectList()` 方法,查询结果。详细解析,见 [「2.3 selectList」](https://svip.iocoder.cn/MyBatis/executor-5/#) 。 - `<2>` 处,调用 `ResultExtractor#extractObjectFromList(List list, Class targetType)` 方法,提取结果。详细解析,见 [「3. ResultExtractor」](https://svip.iocoder.cn/MyBatis/executor-5/#) 。 - `<3>` 处,返回结果。 ## 2.3 selectList `#selectList()` 方法,查询结果。代码如下: ``` // ResultLoader.java private List 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 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(); // 检查缓存中已存在 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); // 如果要求延迟加载,则延迟加载 if (propertyMapping.isLazy()) { // 如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象。 lazyLoader.addLoader(property, metaResultObject, resultLoader); // 返回已定义 value = DEFERED; // 如果不要求延迟加载,则直接执行加载对应的值 } else { value = resultLoader.loadResult(); } } } return value; } ``` - `` 处,因为是结果对象的 setting 方法中使用的值,可以使用延迟加载的功能,所以使用 ResultLoaderMap 记录。最终会创建结果对象的**代理对象**,而 ResultLoaderMap 对象会传入其中,作为一个参数。从而能够,在加载该属性时,能够调用 `ResultLoader#loadResult()` 方法,加载结果。 - 另外,在 `` 处,检查缓存中已存在,则会调用 `Executor#deferLoad(...)` 方法,**尝试**加载结果。代码如下: > 老艿艿:此处是插入,😈 找不到适合放这块内容的地方了,哈哈哈。 ``` // 该方法在 BaseExecutor 抽象类中实现 // BaseExecutor.java /** * DeferredLoad( 延迟加载 ) 队列 */ protected ConcurrentLinkedQueue 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 list = (List) 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 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 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> constructorArgTypes = new ArrayList<>(); // 记录使用的构造方法的参数类型的数组 final List constructorArgs = new ArrayList<>(); // 记录使用的构造方法的参数值的数组 // 创建映射后的结果对象 Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { // 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象 final List 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); // break; } } } // 判断是否使用构造方法创建该结果对象 this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result return resultObject; } ``` - `` 处,调用 `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> constructorArgTypes, List 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 unloadedProperties, ObjectFactory objectFactory, List> constructorArgTypes, List constructorArgs)` 方法,创建支持**反序列化**的代理对象。代码如下: ``` // JavassistProxyFactory.java public Object createDeserializationProxy(Object target, Map unloadedProperties, ObjectFactory objectFactory, List> constructorArgTypes, List constructorArgs) { return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs); } ``` - 因为实际场景下,不太使用该功能,所以本文暂时无视。 ### 5.1.3 createProxy 普通方法 `#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List constructorArgs)` 方法,创建代理对象。代码如下: ``` // JavassistProxyFactory.java @Override public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List constructorArgs) { return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); } ``` ### 5.1.4 crateProxy 静态方法 `#crateProxy(Class type, MethodHandler callback, List> constructorArgTypes, List constructorArgs)` **静态**方法,创建代理对象。代码如下: ``` // JavassistProxyFactory.java static Object crateProxy(Class type, MethodHandler callback, List> constructorArgTypes, List 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); } // 设置代理对象的执行器 ((Proxy) enhanced).setHandler(callback); return enhanced; } ``` - 常见的基于 Javassist 的 API ,创建代理对象。 - `` 处,设置代理对象的执行器。该执行器,就是 EnhancedResultObjectProxyImpl 对象。详细解析,见 [「5.1.5 EnhancedResultObjectProxyImpl」](https://svip.iocoder.cn/MyBatis/executor-5/#) 。 ### 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 lazyLoadTriggerMethods; private final ObjectFactory objectFactory; private final List> constructorArgTypes; private final List constructorArgs; private EnhancedResultObjectProxyImpl(Class type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List 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 lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString")); ``` #### 5.1.5.2 createProxy `#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List> constructorArgTypes, List 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> constructorArgTypes, List 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. 彩蛋 哎哟,比想象中的简单好多。嘿嘿。 参考和推荐如下文章: - 祖大俊 [《Mybatis3.4.x技术内幕(二十一):参数设置、结果封装、级联查询、延迟加载原理分析》](https://my.oschina.net/zudajun/blog/747283) - 无忌 [《MyBatis源码解读之延迟加载》](https://my.oschina.net/wenjinglian/blog/1857581) - 徐郡明 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「3.3.5 嵌套查询&延迟加载」](https://svip.iocoder.cn/MyBatis/executor-5/#) 小节