上一篇文章我們已經將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;
}
重要的邏輯已經註釋出來了。分別如下:
- 創建實體類對象
-
自動映射結果集中有的column,但resultMap中並沒有配置
-
根據 <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;
}
下面先來總結一下該方法的邏輯:
- 根據 nestedQueryId 獲取 MappedStatement
- 生成參數對象
- 獲取 BoundSql
- 創建結果加載器 ResultLoader
- 檢測當前屬性是否需要進行延遲加載,若需要,則添加延遲加載相關的對象到 loaderMap 集合中
- 如不需要延遲加載,則直接通過結果加載器加載結果
以上流程中針對一級緩存的檢查是十分有必要的,若緩存命中,可直接取用結果,無需再在執行關聯查詢 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);
}
}
註釋已經很清楚了,我就不累述了
原文出處:https://www.cnblogs.com/java-chen-hao/p/11760777.html