上一篇文章咱們已經將SQL發送到了數據庫,並返回了ResultSet,接下來就是將結果集 ResultSet 自動映射成實體類對象。這樣使用者就無需再手動操做結果集,並將數據填充到實體類對象中。這可大大下降開發的工做量,提升工做效率。html
咱們來看看上次看源碼的位置java
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方法apache
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的ResultListsession
MyBatis 默認提供了 RowBounds 用於分頁,從上面的代碼中能夠看出,這並不是是一個高效的分頁方式,是查出全部的數據,進行內存分頁。除了使用 RowBounds,還可使用一些第三方分頁插件進行分頁。咱們後面文章來說,咱們來看關鍵代碼getRowValue,處理一行數據app
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中並無配置ide
根據 <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 中。
接下來分析一下 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; }
下面先來總結一下該方法的邏輯:
以上流程中針對一級緩存的檢查是十分有必要的,若緩存命中,可直接取用結果,無需再在執行關聯查詢 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基於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使用的是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); } }
註釋已經很清楚了,我就不累述了