zoukankan      html  css  js  c++  java
  • Mybaits 源码解析 (八)----- 结果集 ResultSet 自动映射成实体类对象(上篇)

    上一篇文章我们已经将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);
      }
    
    }

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

  • 相关阅读:
    UVa 1151 Buy or Build【最小生成树】
    UVa 216 Getting in Line【枚举排列】
    UVa 729 The Hamming Distance Problem【枚举排列】
    HDU 5214 Movie【贪心】
    HDU 5223 GCD
    POJ 1144 Network【割顶】
    UVa 11025 The broken pedometer【枚举子集】
    HDU 2515 Yanghee 的算术【找规律】
    Java基本语法
    Java环境变量,jdk和jre的区别,面向对象语言编程
  • 原文地址:https://www.cnblogs.com/java-chen-hao/p/11760777.html
Copyright © 2011-2022 走看看