上一篇文章我们已经将SQL发送到了数据库,并返回了ResultSet,接下来就是将结果集 ResultSet 自动映射成实体类对象。这样使用者就无需再手动操作结果集,并将数据填充到实体类对象中。这可大大降低开发的工作量,提高工作效率。

映射结果入口

我们来看看上次看源码的位置

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
//执行数据库SQL
ps.execute();
//进行resultSet自动映射
return this.resultSetHandler.handleResultSets(ps);
}

结果集的处理入口方法是 handleResultSets

public List<Object> handleResultSets(Statement stmt) throws SQLException {

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
//获取第一个ResultSet,通常只会有一个
ResultSetWrapper rsw = getFirstResultSet(stmt);
//从配置中读取对应的ResultMap,通常也只会有一个,设置多个是通过逗号来分隔,我们平时有这样设置吗?
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集
handleResultSet(rsw, resultMap, multipleResults, null
);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
} // 以下逻辑均与多结果集有关,就不分析了,代码省略
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {...} return collapseSingleResultList(multipleResults);
}

在实际运行过程中,通常情况下一个Sql语句只返回一个结果集,对多个结果集的情况不做分析 。实际很少用到。继续看handleResultSet方法

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 创建默认的结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理结果集的行数据
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 将结果加入multipleResults中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
closeResultSet(rsw.getResultSet());
}
}

通过handleRowValues 映射ResultSet结果,最后映射的结果会在defaultResultHandler的ResultList集合中,最后将结果加入到multipleResults中就可以返回了,我们继续跟进handleRowValues这个核心方法

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
// 处理嵌套映射,关于嵌套映射我们下一篇文章单独分析
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 处理简单映射,本文先只分析简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}

我们可以通过resultMap.hasNestedResultMaps()知道查询语句是否是嵌套查询,如果resultMap中包含<association> 和 <collection>且其select属性不为空,则为嵌套查询,大家可以看看我第三篇文章关于解析 resultMap 节点。本文先分析简单的映射

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,
ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// 根据 RowBounds 定位到指定行记录
skipRows(rsw.getResultSet(), rowBounds);
// ResultSet是一个集合,很有可能我们查询的就是一个List,这就就每条数据遍历处理
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 从 resultSet 中获取结果
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存储结果到resultHandler的ResultList,最后ResultList加入multipleResults中返回
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}

我们查询的结果很有可能是一个集合,所以这里要遍历集合,每条结果单独进行映射,最后映射的结果加入到resultHandler的ResultList

MyBatis 默认提供了 RowBounds 用于分页,从上面的代码中可以看出,这并非是一个高效的分页方式,是查出所有的数据,进行内存分页。除了使用 RowBounds,还可以使用一些第三方分页插件进行分页。我们后面文章来讲,我们来看关键代码getRowValue,处理一行数据

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
// 这个Map是用来存储延迟加载的BountSql的,我们下面来看
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建实体类对象,比如 Employ 对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings; if (shouldApplyAutomaticMappings(resultMap, false)) {
//自动映射,结果集中有的column,但resultMap中并没有配置
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 根据 <resultMap> 节点中配置的映射关系进行映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) ||
foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}

重要的逻辑已经注释出来了。分别如下:

  1. 创建实体类对象
  2. 自动映射结果集中有的column,但resultMap中并没有配置

  3. 根据 <resultMap> 节点中配置的映射关系进行映射

创建实体类对象

我们想将查询结果映射成实体类对象,第一步当然是要创建实体类对象了,下面我们来看一下 MyBatis 创建实体类对象的过程。

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    this.useConstructorMappings = false;
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>(); // 调用重载方法创建实体类对象
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) {
// 如果开启了延迟加载,则为 resultObject 生成代理类,如果仅仅是配置的关联查询,没有开启延迟加载,是不会创建代理类
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
/*
* 创建代理类,默认使用 Javassist 框架生成代理类。
* 由于实体类通常不会实现接口,所以不能使用 JDK 动态代理 API 为实体类生成代理。
* 并且将lazyLoader传进去了
*/

resultObject = configuration.getProxyFactory()
.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings =
resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}

我们先来看 createResultObject 重载方法的逻辑

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {

    final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 通过 ObjectFactory 调用目标类的默认构造方法创建实例
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

一般情况下,MyBatis 会通过 ObjectFactory 调用默认构造方法创建实体类对象。看看是如何创建的

public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Class<?> classToCreate = this.resolveInterface(type);
return this.instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
} <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor constructor;
if (constructorArgTypes != null && constructorArgs != null) {
constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
} return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
//通过反射获取构造器
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
//通过构造器来实例化对象
return
constructor.newInstance();
}
} catch (Exception var9) {
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
}
}

很简单,就是通过反射创建对象

结果集映射

映射结果集分为两种情况:一种是自动映射(结果集有但在resultMap里没有配置的字段),在实际应用中,都会使用自动映射,减少配置的工作。自动映射在Mybatis中也是默认开启的。第二种是映射ResultMap中配置的,我们分这两者映射来看

自动映射

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

    // 获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 通过 TypeHandler 从结果集中获取指定列的数据
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// 通过元信息对象设置 value 到实体类对象的指定字段上
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}

首先是获取 UnMappedColumnAutoMapping 集合,然后遍历该集合,并通过 TypeHandler 从结果集中获取数据,最后再将获取到的数据设置到实体类对象中。

UnMappedColumnAutoMapping 用于记录未配置在 <resultMap> 节点中的映射关系。它的代码如下:

private static class UnMappedColumnAutoMapping {

    private final String column;
private final String property;
private final TypeHandler<?> typeHandler;
private final boolean primitive; public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
this.column = column;
this.property = property;
this.typeHandler = typeHandler;
this.primitive = primitive;
}
}

仅用于记录映射关系。下面看一下获取 UnMappedColumnAutoMapping 集合的过程,如下:

private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

    final String mapKey = resultMap.getId() + ":" + columnPrefix;
// 从缓存中获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
// 缓存未命中
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
// 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
// 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
// 检测当前属性是否存在于 resultMap 中
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
// 获取属性对于的类型
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// 封装上面获取到的信息到 UnMappedColumnAutoMapping 对象中
autoMapping.add(new
UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
// 写入缓存
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}

先来看看从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名

public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (unMappedColumnNames == null) {
// 加载已映射与未映射列名
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
// 获取未映射列名
unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return unMappedColumnNames;
} private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<String>();
List<String> unmappedColumnNames = new ArrayList<String>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
// 获取 <resultMap> 中配置的所有列名
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
/*
* 遍历 columnNames,columnNames 是 ResultSetWrapper 的成员变量,保存了当前结果集中的所有列名
* 这里是通过ResultSet中的所有列名来获取没有在resultMap中配置的列名
* 意思是后面进行自动赋值时,只赋值查出来的列名
*/

for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
// 检测已映射列名集合中是否包含当前列名
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
// 将列名存入 unmappedColumnNames 中
unmappedColumnNames.add(columnName);
}

}
// 缓存列名集合
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}

首先是从当前数据集中获取列名集合,然后获取 <resultMap> 中配置的列名集合。之后遍历数据集中的列名集合,并判断列名是否被配置在了 <resultMap> 节点中。若配置了,则表明该列名已有映射关系,此时该列名存入 mappedColumnNames 中。若未配置,则表明列名未与实体类的某个字段形成映射关系,此时该列名存入 unmappedColumnNames 中。

映射result节点

接下来分析一下 MyBatis 是如何将结果集中的数据填充到已配置ResultMap映射的实体类字段中的。

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    // 获取已映射的列名
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 获取 ResultMapping集合
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
// 所有的ResultMapping遍历进行映射
for
(ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) { // 从结果集中获取指定列的数据
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
final String property = propertyMapping.getProperty();
if (property == null) {
continue; // 若获取到的值为 DEFERED,则延迟加载该值
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// 将获取到的值设置到实体类对象中
metaObject.setValue(property, value);
}
}
}
return foundValues;
} private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { if (propertyMapping.getNestedQueryId() != null) {
// 获取关联查询结果
return
getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 从 ResultSet 中获取指定列的值
return
typeHandler.getResult(rs, column);
}
}

从 ResultMap 获取映射对象 ResultMapping 集合。然后遍历 ResultMapping 集合,再此过程中调用 getPropertyMappingValue 获取指定指定列的数据,最后将获取到的数据设置到实体类对象中。

这里和自动映射有一点不同,自动映射是从直接从ResultSet 中获取指定列的值,但是通过ResultMap多了一种情况,那就是关联查询,也可以说是延迟查询,此关联查询如果没有配置延迟加载,那么就要获取关联查询的值,如果配置了延迟加载,则返回DEFERED

关联查询与延迟加载

我们的查询经常会碰到一对一,一对多的情况,通常我们可以用一条 SQL 进行多表查询完成任务。当然我们也可以使用关联查询,将一条 SQL 拆成两条去完成查询任务。MyBatis 提供了两个标签用于支持一对一和一对多的使用场景,分别是 <association> 和 <collection>。下面我来演示一下如何使用 <association> 完成一对一的关联查询。先来看看实体类的定义:

/** 作者类 */
public class Author {
private Integer id;
private String name;
private Integer age;
private Integer sex;
private String email; // 省略 getter/setter
} /** 文章类 */
public class Article {
private Integer id;
private String title;
// 一对一关系
private Author author;
private String content;
private Date createTime; // 省略 getter/setter
}

接下来看一下 Mapper 接口与映射文件的定义。

public interface ArticleDao {
Article findOne(@Param("id") int id);
Author findAuthor(@Param("id") int authorId);
}
<mapper namespace="xyz.coolblog.dao.ArticleDao">
<resultMap id="articleResult" type="Article">
<result property="createTime" column="create_time"/>
//column 属性值仅包含列信息,参数类型为 author_id 列对应的类型,这里为 Integer
//意思是将author_id做为参数传给关联的查询语句findAuthor
<association property="author" column="author_id" javaType="Author" select="findAuthor"/>

</resultMap> <select id="findOne" resultMap="articleResult">
SELECT
id, author_id, title, content, create_time
FROM
article
WHERE
id = #{id}
</select> <select id="findAuthor" resultType="Author">
SELECT
id, name, age, sex, email
FROM
author
WHERE
id = #{id}
</select>
</mapper>

开启延迟加载

<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭积极的加载策略 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 延迟加载的触发方法 -->
<setting name="lazyLoadTriggerMethods" value="equals,hashCode"/>

此时association节点使用了select指向另外一个查询语句,并且将 author_id作为参数传给关联查询的语句

此时如果不开启延迟加载,那么会生成两条SQL,先执行findOne,然后通过findOne的返回结果做为参数,执行findAuthor语句,并将结果设置到author属性

如果开启了延迟加载呢?那么只会执行findOne一条SQL,当调用article.getAuthor()方法时,才会去执行findAuthor进行查询,我们下面来看看是如何实现的

我们还是要从上面映射result节点说起

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    if (propertyMapping.getNestedQueryId() != null) {
// 获取关联查询结果
return
getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 从 ResultSet 中获取指定列的值
return typeHandler.getResult(rs, column);
}
}

我们看到,如果ResultMapping设置了关联查询,也就是association或者collection配置了select,那么就要通过关联语句来查询结果,并设置到实体类对象的属性中了。如果没配置select,那就简单,直接从ResultSet中通过列名获取结果。那我们来看看getNestedQueryMappingValue

private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    // 获取关联查询 id,id = 命名空间 + <association> 的 select 属性值
final String nestedQueryId = propertyMapping.getNestedQueryId();
final String property = propertyMapping.getProperty();
// 根据 nestedQueryId 获取关联的 MappedStatement
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
//获取关联查询MappedStatement的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
/*
* 生成关联查询语句参数对象,参数类型可能是一些包装类,Map 或是自定义的实体类,
* 具体类型取决于配置信息。以上面的例子为基础,下面分析不同配置对参数类型的影响:
* 1. <association column="author_id">
* column 属性值仅包含列信息,参数类型为 author_id 列对应的类型,这里为 Integer
*
* 2. <association column="{id=author_id, name=title}">
* column 属性值包含了属性名与列名的复合信息,MyBatis 会根据列名从 ResultSet 中
* 获取列数据,并将列数据设置到实体类对象的指定属性中,比如:
* Author{id=1, name="陈浩"}
* 或是以键值对 <属性, 列数据> 的形式,将两者存入 Map 中。比如:
* {"id": 1, "name": "陈浩"}
*
* 至于参数类型到底为实体类还是 Map,取决于关联查询语句的配置信息。比如:
* <select id="findAuthor"> -> 参数类型为 Map
* <select id="findAuthor" parameterType="Author"> -> 参数类型为实体类
*/
final Object nestedQueryParameterObject =
prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
// 获取 BoundSql,这里设置了运行时参数,所以这里是能直接执行的
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType(); if (executor.isCached(nestedQuery, key)) {
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
value = DEFERED;
} else {
// 创建结果加载器
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// 检测当前属性是否需要延迟加载
if (propertyMapping.isLazy()) {
// 添加延迟加载相关的对象到 loaderMap 集合中
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERED;
} else {
// 直接执行关联查询
// 如果只是配置关联查询,但是没有开启懒加载,则直接执行关联查询,并返回结果,设置到实体类对象的属性中
value =
resultLoader.loadResult();
}

}
}
return value;
}

下面先来总结一下该方法的逻辑:

  1. 根据 nestedQueryId 获取 MappedStatement
  2. 生成参数对象
  3. 获取 BoundSql
  4. 创建结果加载器 ResultLoader
  5. 检测当前属性是否需要进行延迟加载,若需要,则添加延迟加载相关的对象到 loaderMap 集合中
  6. 如不需要延迟加载,则直接通过结果加载器加载结果

以上流程中针对一级缓存的检查是十分有必要的,若缓存命中,可直接取用结果,无需再在执行关联查询 SQL。若缓存未命中,接下来就要按部就班执行延迟加载相关逻辑

我们来看一下添加延迟加载相关对象到 loaderMap 集合中的逻辑,如下:

public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
// 将属性名转为大写
String upperFirst = getUppercaseFirstProperty(property);
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,并将 <大写属性名,LoadPair对象> 键值对添加到 loaderMap 中
loaderMap.put(upperFirst, new
LoadPair(property, metaResultObject, resultLoader));
}

我们再来回顾一下文章开始的创建实体类

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    this.useConstructorMappings = false;
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>(); // 调用重载方法创建实体类对象
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) {
// 如果开启了延迟加载,则为 resultObject 生成代理类,如果仅仅是配置的关联查询,没有开启延迟加载,是不会创建代理类
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
/*
* 创建代理类,默认使用 Javassist 框架生成代理类。
* 由于实体类通常不会实现接口,所以不能使用 JDK 动态代理 API 为实体类生成代理。
* 并且将lazyLoader传进去了
*/
resultObject = configuration.getProxyFactory()
.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings =
resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}

如果开启了延迟加载,并且有关联查询,此时是要创建一个代理对象的,将上面存放BondSql的lazyLoader和创建的目标对象resultObject 作为参数传进去。

Mybatis提供了两个实现类CglibProxyFactory和JavassistProxyFactory,分别基于org.javassist:javassist和cglib:cglib进行实现。createProxy方法就是实现懒加载逻辑的核心方法,也是我们分析的目标。

CglibProxyFactory

CglibProxyFactory基于cglib动态代理模式,通过继承父类的方式生成动态代理类。

@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);
} public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
//由CglibProxyFactory生成对象
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
//复制属性
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
} static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(callback);
//设置父类对象
enhancer.setSuperclass(type);
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// 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});
} catch (SecurityException e) {
// nothing to do here
}
Object enhanced;
if (constructorArgTypes.isEmpty()) {
enhanced = enhancer.create();
} else {
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
enhanced = enhancer.create(typesArray, valuesArray);
}
return enhanced;
}

可以看到,初始化Enhancer,并调用构造方法,生成对象。从enhancer.setSuperclass(type);也能看出cglib采用的是继承父类的方式。

EnhancedResultObjectProxyImpl

EnhancedResultObjectProxyImpl实现了MethodInterceptor接口,此接口是Cglib拦截目标对象方法的入口,对目标对象方法的调用都会通过此接口的intercept的方法。

@Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
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 CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
/*
* 如果 aggressive 为 true,或触发方法(比如 equals,hashCode 等)被调用,
* 则加载所有的所有延迟加载的数据
*/
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
// 如果使用者显示调用了 setter 方法,则将相应的延迟加载类从 loaderMap 中移除
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
// 检测使用者是否调用 getter 方法
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
// 执行延迟加载逻辑
lazyLoader.load(property);
}
}

}
}
}
//执行原方法(即父类方法)
return methodProxy.invokeSuper(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}

完整的代码

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set; import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.executor.loader.ResultLoaderMap;
import org.apache.ibatis.executor.loader.WriteReplaceInterface;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.property.PropertyCopier;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.apache.ibatis.session.Configuration; /**
* cglib代理工厂类,实现延迟加载属性
* @author Clinton Begin
*/
public class CglibProxyFactory implements ProxyFactory { /**
* finalize方法
*/
private static final String FINALIZE_METHOD = "finalize";
/**
* writeReplace方法
*/
private static final String WRITE_REPLACE_METHOD = "writeReplace"; /**
* 加载Enhancer,这个是Cglib的入口
*/
public CglibProxyFactory() {
try {
Resources.classForName("net.sf.cglib.proxy.Enhancer");
} catch (Throwable e) {
throw new IllegalStateException("Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.", e);
}
} /**
* 创建代理对象
* @param target 目标对象
* @param lazyLoader 延迟加载器
* @param configuration 配置类
* @param objectFactory 对象工厂
* @param constructorArgTypes 构造函数类型[]
* @param constructorArgs 构造函数的值[]
* @return
*/
@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);
} /**
* 创建一个反序列化代理
* @param target 目标
* @param unloadedProperties
* @param objectFactory 对象工厂
* @param constructorArgTypes 构造函数类型数组
* @param constructorArgs 构造函数值
* @return
*/
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);
} @Override
public void setProperties(Properties properties) {
// Not Implemented
} /**
* 返回代理对象, 这个代理对象在调用任何方法都会调用本类的intercept方法
* Enhancer 认为这个就是自定义类的工厂,比如这个类需要实现什么接口
* @param type 目标类型
* @param callback 结果对象代理实现类,当中有invoke回调方法
* @param constructorArgTypes 构造函数类型数组
* @param constructorArgs 构造函数对应字段的值数组
* @return
*/
static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// enhancer 配置调节代理对象的一些参数
// 设置回调方法
// 设置超类
//判断当传入目标类型是否有writeReplace方法,没有则配置一个有writeReplace方法的接口(序列化写出)
Enhancer enhancer = new Enhancer();
enhancer.setCallback(callback);
enhancer.setSuperclass(type);
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (LogHolder.log.isDebugEnabled()) {
LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
//这个enhancer增加一个WriteReplaceInterface接口
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// nothing to do here
}
//根据构造函数创建一个对象
//无参构造
//有参构造
Object enhanced;
if (constructorArgTypes.isEmpty()) {
enhanced = enhancer.create();
} else {
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
enhanced = enhancer.create(typesArray, valuesArray);
}
return enhanced;
} /**
* 结果对象代理实现类,
* 它实现方法拦截器的intercept方法
*/
private static class EnhancedResultObjectProxyImpl implements MethodInterceptor { 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; /**
* 代理对象创建
* @param type 目标class类型
* @param lazyLoader 延迟加载器
* @param configuration 配置信息
* @param objectFactory 对象工厂
* @param constructorArgTypes 构造函数类型数组
* @param 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;
} /**
* 创建代理对象, 将源对象值赋值给代理对象
* @param target 目标对象
* @param lazyLoader 延迟加载器
* @param configuration 配置对象
* @param objectFactory 对象工厂
* @param constructorArgTypes 构造函数类型数组
* @param constructorArgs 构造函数值数组
* @return
*/
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//获取目标的类型
//创建一个结果对象代理实现类(它实现cglib的MethodInterface接口,完成回调作用invoke方法)
final Class<?> type = target.getClass();
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
} /**
* 回调方法
* @param enhanced 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//获取方法名
final String methodName = method.getName();
try {
// 同步获取延迟加载对象
// 如果是执行writeReplace方法(序列化写出)
// 实例化一个目标对象的实例
synchronized (lazyLoader) {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
// 将enhanced中的属性复制到orignal对象中
// 如果延迟加载数量>0,
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
//不是writeReplace方法
// 延迟加载长度大于0, 且不是finalize方法
// configuration配置延迟加载参数,延迟加载触发的方法包含这个方法
// 延迟加载所有数据
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
// setter方法,直接移除
} else if (PropertyNamer.isSetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
// getter方法, 加载该属性
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
}
return methodProxy.invokeSuper(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
} /**
* 他继承抽象反序列化代理和实现了方法拦截
*/
private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodInterceptor { private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
} /**
* 创建代理对象
* @param target
* @param unloadedProperties
* @param objectFactory
* @param constructorArgTypes
* @param constructorArgs
* @return
*/
public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
} @Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
final Object o = super.invoke(enhanced, method, args);
return o instanceof AbstractSerialStateHolder ? o : methodProxy.invokeSuper(o, args);
} @Override
protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return new CglibSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
} }

如上,代理方法首先会检查 aggressive 是否为 true,如果不满足,再去检查 lazyLoadTriggerMethods 是否包含当前方法名。这里两个条件只要一个为 true,当前实体类中所有需要延迟加载。aggressive 和 lazyLoadTriggerMethods 两个变量的值取决于下面的配置。

<setting name="aggressiveLazyLoading" value="false"/>
<setting name="lazyLoadTriggerMethods" value="equals,hashCode"/>

然后代理逻辑会检查使用者是不是调用了实体类的 setter 方法,如果调用了,就将该属性对应的 LoadPair 从 loaderMap 中移除。为什么要这么做呢?答案是:使用者既然手动调用 setter 方法,说明使用者想自定义某个属性的值。此时,延迟加载逻辑不应该再修改该属性的值,所以这里从 loaderMap 中移除属性对于的 LoadPair。

最后如果使用者调用的是某个属性的 getter 方法,且该属性配置了延迟加载,此时延迟加载逻辑就会被触发。那接下来,我们来看看延迟加载逻辑是怎样实现的的。

public boolean load(String property) throws SQLException {
// 从 loaderMap 中移除 property 所对应的 LoadPair
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
if (pair != null) {
// 加载结果
pair.load();
return true;
}
return false;
} public void load(final Object userObject) throws SQLException {
/*
* 调用 ResultLoader 的 loadResult 方法加载结果,
* 并通过 metaResultObject 设置结果到实体类对象中
*/
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
} public Object loadResult() throws SQLException {
// 执行关联查询
List<Object> list = selectList();
// 抽取结果
resultObject = resultExtractor.extractObjectFromList(list, targetType);
return resultObject;
} private <E> List<E> selectList() throws SQLException {
Executor localExecutor = executor;
if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
localExecutor = newExecutor();
}
try {
// 通过 Executor 就行查询,这个之前已经分析过了
// 这里的parameterObject和boundSql就是我们之前存放在LoadPair中的,现在直接拿来执行了
return localExecutor.<E>
query(mappedStatement, parameterObject, RowBounds.DEFAULT,
Executor.NO_RESULT_HANDLER, cacheKey, boundSql);

} finally {
if (localExecutor != executor) {
localExecutor.close(false);
}
}
}

好了,延迟加载我们基本已经讲清楚了,我们介绍一下另外的一种代理方式

JavassistProxyFactory

JavassistProxyFactory使用的是javassist方式,直接修改class文件的字节码格式。

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set; import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory; import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
import org.apache.ibatis.executor.loader.ResultLoaderMap;
import org.apache.ibatis.executor.loader.WriteReplaceInterface;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.property.PropertyCopier;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.apache.ibatis.session.Configuration; /**JavassistProxy字节码生成代理
* 1.创建一个代理对象然后将目标对象的值赋值给代理对象,这个代理对象是可以实现其他的接口
* 2. JavassistProxyFactory实现ProxyFactory接口createProxy(创建代理对象的方法)
* @author Eduardo Macarron
*/
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory { /**
* finalize方法(垃圾回收)
*/
private static final String FINALIZE_METHOD = "finalize"; /**
* writeReplace(序列化写出方法)
*/
private static final String WRITE_REPLACE_METHOD = "writeReplace"; /**
* 加载ProxyFactory, 也就是JavassistProxy的入口
*/
public JavassistProxyFactory() {
try {
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);
}
} /**
* 创建代理
* @param target 目标对象
* @param lazyLoader 延迟加载Map集合(那些属性是需要延迟加载的)
* @param configuration 配置类
* @param objectFactory 对象工厂
* @param constructorArgTypes 构造函数类型[]
* @param constructorArgs 构造函数的值[]
* @return
*/
@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);
} 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);
} @Override
public void setProperties(Properties properties) {
// Not Implemented
} /**
* 获取代理对象, 也就是说在执行方法之前首先调用MethodHanlder的invoke方法
* @param type 目标类型
* @param callback 回调对象
* @param constructorArgTypes 构造函数类型数组
* @param constructorArgs 构造函数值的数组
* @return
*/
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 创建一个代理工厂类
// 配置超类
ProxyFactory enhancer = new ProxyFactory();
enhancer.setSuperclass(type);
//判断是否有writeReplace方法,如果没有将这个代理对象实现WriteReplaceInterface接口,这个接口只有一个writeReplace方法
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (LogHolder.log.isDebugEnabled()) {
LogHolder.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});
} 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的MethodHandler接口, 相对于Cglib的MethodInterceptor
* 他们接口的方法名也是不一样的,Javassist的是invoke, 而cglib是intercept,叫法不同,实现功能是一样的
*/
private static class EnhancedResultObjectProxyImpl implements MethodHandler { /**
* 目标类型
*/
private final Class<?> type;
/**
* 延迟加载Map集合
*/
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; /**
* 构造函数私有化了
* @param type
* @param lazyLoader
* @param configuration
* @param objectFactory
* @param constructorArgTypes
* @param 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;
} public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 获取目标类型
// 创建一个EnhancedResultObjectProxyImpl对象,回调对象
final Class<?> type = target.getClass();
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
} /**
* 回调方法
* @param enhanced 代理对象
* @param method 方法
* @param methodProxy 代理方法
* @param args 入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
//获取方法名称
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
//如果方法是writeReplace
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
//不是writeReplace方法
// 延迟加载长度大于0, 且不是finalize方法
// configuration配置延迟加载参数,延迟加载触发的方法包含这个方法
// 延迟加载所有数据
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
} private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodHandler { private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
} public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
} @Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final Object o = super.invoke(enhanced, method, args);
return o instanceof AbstractSerialStateHolder ? o : methodProxy.invoke(o, args);
} @Override
protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return new JavassistSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
} private static class LogHolder {
private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);
} }

注释已经很清楚了,我就不累述了

Mybaits 源码解析 (八)----- 全网最详细,没有之一:结果集 ResultSet 自动映射成实体类对象(上篇)的更多相关文章

  1. Mybaits 源码解析 (三)----- Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    上一篇我们讲解到mapperElement方法用来解析mapper,我们这篇文章具体来看看mapper.xml的解析过程 mappers配置方式 mappers 标签下有许多 mapper 标签,每一 ...

  2. Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)

    SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api.myabtis提供了两个SqlSesion接口的实现,常用的实现类是De ...

  3. Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析

    像Mybatis.Hibernate这样的ORM框架,封装了JDBC的大部分操作,极大的简化了我们对数据库的操作. 在实际项目中,我们发现在一个事务中查询同样的语句两次的时候,第二次没有进行数据库查询 ...

  4. Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)

    上一篇我们分析了Mapper接口代理类的生成,本篇接着分析是如何调用到XML中的SQL 我们回顾一下MapperMethod 的execute方法 public Object execute(SqlS ...

  5. Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析

    在前面几篇文章中我们主要分析了Mybatis的单独使用,在实际在常规项目开发中,大部分都会使用mybatis与Spring结合起来使用,毕竟现在不用Spring开发的项目实在太少了.本篇文章便来介绍下 ...

  6. Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一

    我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...

  7. Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    刚开始使用Mybaits的同学有没有这样的疑惑,为什么我们没有编写Mapper的实现类,却能调用Mapper的方法呢?本篇文章我带大家一起来解决这个疑问 上一篇文章我们获取到了DefaultSqlSe ...

  8. Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?

    不知道一些同学有没有这种疑问,为什么Mybtis中要配置dataSource,Spring的事务中也要配置dataSource?那么Mybatis和Spring事务中用的Connection是同一个吗 ...

  9. Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring

    上一篇文章我们讲了SqlSessionFactoryBean,通过这个FactoryBean创建SqlSessionFactory并注册进Spring容器,这篇文章我们就讲剩下的部分,通过Mapper ...

随机推荐

  1. 43 道检验基础的 JavaScript 面试题

    导读 这两天的GitHub Trending repositories被一个名叫 javascript-questions的项目霸榜了,项目中记录了一些JavaScript题目. 文中有些点作者解释的 ...

  2. Scala 学习笔记之集合(5)

    import collection.mutable.Buffer object CollectionDemo6 { def main(args: Array[String]): Unit = { // ...

  3. MongoDB 学习笔记之 权限管理基础

    权限管理基础 MongoDB有很多用户roles,这里只是简单列举下命令的使用,具体的role的含义,请查阅官方文档. https://docs.mongodb.com/manual/referenc ...

  4. php数字函数

    is_numeric() 检查变量是否包含一个合法数字 round()  取整数,四舍五入 round(数字, 小数位) ceil()  向上取整 floor() 向下取整 range()  生成范围 ...

  5. ui自动化测试

    一.梳理 1.根据要求需要自动添加很多条数据 2.这就涉及到ui方面的知识.元素定位的方法(这个就能遇到很多坑,要完全掌握元素定位才能避免进坑).循环等(代码基础要掌握好) 二.操作 选择进行自动化操 ...

  6. Cocos Creator 通用框架设计 —— 网络

    在Creator中发起一个http请求是比较简单的,但很多游戏希望能够和服务器之间保持长连接,以便服务端能够主动向客户端推送消息,而非总是由客户端发起请求,对于实时性要求较高的游戏更是如此.这里我们会 ...

  7. asp.net core 3.0 中使用 swagger

    asp.net core 3.0 中使用 swagger Intro 上次更新了 asp.net core 3.0 简单的记录了一下 swagger 的使用,那个项目的 api 比较简单,都是匿名接口 ...

  8. MySQL优化与实践

    一.MySQL优化概括 二.SQL优化 实践: 1.查看是否开启了慢查询日志 show variables like 'slow_query_log' 没有开启 2.查看是否开启了未使用索引SQL记录 ...

  9. Java序列化总结(最全)

    概念 实现 Serializable 接口, 它只是一个标记接口,不实现也能够进行序列化 RMI: 远程方法调用 RPC: 远程过程调用 序列化ID 解决了序列化与反序列出现代码不一致的问题, 不一致 ...

  10. Roadmap of FE

    未完待补充......