Mybaits 源碼解析 (八)—– 全網最詳細,沒有之一:結果集 ResultSet 自動映射成實體類對象(上篇)

  • 2019 年 11 月 8 日
  • 筆記

上一篇文章我們已經將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);      }    }      }

View Code

如上,代理方法首先會檢查 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);    }    }

注釋已經很清楚了,我就不累述了