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

923 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 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();
// <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」](https://svip.iocoder.cn/MyBatis/executor-5/#) 。
- `<2>` 处,调用 `ResultExtractor#extractObjectFromList(List<Object> list, Class<?> targetType)` 方法,提取结果。详细解析,见 [「3. ResultExtractor」](https://svip.iocoder.cn/MyBatis/executor-5/#) 。
- `<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」](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<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 实现类。
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/#) 小节