Mybatis中的动态标签是如何处理的

// 动态SQL标签的解析器
public class XMLScriptBuilder extends BaseBuilder {
    // 映射语句的根节点信息 <select id="xxx"></select>
    public final XNode context;
    // 是否是动态的,如果节点包含${}或者动态标签<if>等,表示动态的,否则为非动态
    public boolean isDynamic;
    // 参数类型
    public final Class<?> parameterType;
    // 所有的SQL标签处理器
    public final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();


    public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
        super(configuration);
        this.context = context;
        this.parameterType = parameterType;
        this.initNodeHandlerMap();
    }

    // 初始话不同的动态SQL标签的处理器
    public void initNodeHandlerMap() {
        nodeHandlerMap.put("trim", new TrimHandler());
        nodeHandlerMap.put("where", new WhereHandler());
        nodeHandlerMap.put("set", new SetHandler());
        nodeHandlerMap.put("foreach", new ForEachHandler());
        nodeHandlerMap.put("if", new IfHandler());
        nodeHandlerMap.put("choose", new ChooseHandler());
        nodeHandlerMap.put("when", new IfHandler());
        nodeHandlerMap.put("otherwise", new OtherwiseHandler());
        nodeHandlerMap.put("bind", new BindHandler());
    }

    // 解析动态SQL标签
    public SqlSource parseScriptNode() {
        // 解析所有动态标签节点,返回根节点<select>中的所有子标签节点
        SqlNode.MixedSqlNode rootSqlNode = this.parseDynamicTags(context);
        SqlSource sqlSource;
        // 当SQL中存在${}或者动态标签<if>等标签,表示该SQL是动态SQL,需要在执行的时候进行解析
        if (isDynamic) {
            // 返回动态的SQL源信息
            sqlSource = new SqlSource.DynamicSqlSource(configuration, rootSqlNode);
        } else {
            // 原始的SQL分类,该SQL表示可以一个直接能使用的SQL或者具有#{}预编译占位符的SQL
            sqlSource = new SqlSource.RawSqlSource(configuration, rootSqlNode, parameterType);
        }
        return sqlSource;
    }

    /**
     * 解析动态标签
     *
     * @param node 从映射语句的根标签 <select id="xxx"></select>开始,递归解析子标签,因为标签可以嵌套
     * @return 返回一个包含的所有SQL节点的混合节点
     */
    protected SqlNode.MixedSqlNode parseDynamicTags(XNode node) {
        List<SqlNode> contents = new ArrayList<>();
        // 获取所有的子节点
        NodeList children = node.getNode().getChildNodes();
        // 遍历所有的子节点
        for (int i = 0; i < children.getLength(); i++) {
            // 获取到子节点
            XNode child = node.newXNode(children.item(i));
            // 如果当前子节点的是<CDATA>这种类型,或者是文本类型,就可以直接解析,否则的话表示其他SQL标签,那么就需要递归去解析这些嵌套标签
            if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
                // 获取到文本内容
                String data = child.getStringBody("");
                // 封装成文本的Sql节点
                SqlNode.TextSqlNode textSqlNode = new SqlNode.TextSqlNode(data);
                // 如果包含${}表示是动态的,否则不是
                if (textSqlNode.isDynamic()) {
                    // 保存该动态的文本节点
                    contents.add(textSqlNode);
                    // 标记当前SQL是一个动态的,不能直接使用,还需要解析${}占位符
                    isDynamic = true;
                } else {
                    // 如果不包含${},表示该节点是一个静态的SQL节点,不需要再次解析了
                    contents.add(new SqlNode.StaticTextSqlNode(data));
                }
            }
            // 如果当前子节点也是一个标签
            else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
                // 获取标签名
                String nodeName = child.getNode().getNodeName();
                // 使用标签名对应的处理器进行除开该节点
                NodeHandler handler = nodeHandlerMap.get(nodeName);
                // 如果不是系统提供的标签,抛出异常
                if (handler == null) {
                    throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
                }
                // 使用节点处理器递归处理
                handler.handleNode(child, contents);
                // 以上这些节点也要标记为动态SQL节点
                isDynamic = true;
            }
        }
        // 返回当前正在处理的节点下的所有子节点信息
        return new SqlNode.MixedSqlNode(contents);
    }

    public interface NodeHandler {
        void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
    }

    // <bind>标签处理器
    public static class BindHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 获取名称和value属性
            final String name = nodeToHandle.getStringAttribute("name");
            final String expression = nodeToHandle.getStringAttribute("value");
            // 创建一个变量定义的SQL节点,该节点可以定义标签中可以使用的变量
            final SqlNode.VarDeclSqlNode node = new SqlNode.VarDeclSqlNode(name, expression);
            // 保存当前节点信息
            targetContents.add(node);
        }
    }

    // <trim>标签处理器
    public class TrimHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<trim>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 获取标签属性
            String prefix = nodeToHandle.getStringAttribute("prefix");
            String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
            String suffix = nodeToHandle.getStringAttribute("suffix");
            String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
            // 封装为TrimSqlNode进行解析
            SqlNode.TrimSqlNode trim = new SqlNode.TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
            // 保存当前节点信息
            targetContents.add(trim);
        }
    }

    // <where>标签处理器
    public class WhereHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<where>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 封装成WhereSqlNode进行解析
            SqlNode.WhereSqlNode where = new SqlNode.WhereSqlNode(configuration, mixedSqlNode);
            // 保存当前节点信息
            targetContents.add(where);
        }
    }

    // <set>标签处理器
    public class SetHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<set>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 封装为SetSqlNode进行解析
            SqlNode.SetSqlNode set = new SqlNode.SetSqlNode(configuration, mixedSqlNode);
            // 保存当前节点信息
            targetContents.add(set);
        }
    }

    // <foreach>标签处理器
    public class ForEachHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<foreach>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 获取标签中的属性
            String collection = nodeToHandle.getStringAttribute("collection");
            Boolean nullable = nodeToHandle.getBooleanAttribute("nullable");
            String item = nodeToHandle.getStringAttribute("item");
            String index = nodeToHandle.getStringAttribute("index");
            String open = nodeToHandle.getStringAttribute("open");
            String close = nodeToHandle.getStringAttribute("close");
            String separator = nodeToHandle.getStringAttribute("separator");
            // 封装为ForEachSqlNode进行解析
            SqlNode.ForEachSqlNode forEachSqlNode = new SqlNode.ForEachSqlNode(configuration, mixedSqlNode, collection, nullable, index, item, open, close, separator);
            // 保存当前节点信息
            targetContents.add(forEachSqlNode);
        }
    }

    // <if>标签处理器
    public class IfHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<if>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 获取if的表达式
            String test = nodeToHandle.getStringAttribute("test");
            // 封装为IfSqlNode进行解析
            SqlNode.IfSqlNode ifSqlNode = new SqlNode.IfSqlNode(mixedSqlNode, test);
            // 保存当前节点信息
            targetContents.add(ifSqlNode);
        }
    }

    // <otherwise>标签处理器
    public class OtherwiseHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析<otherwise>内部动态SQL标签
            SqlNode.MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 直接保存内部的节点,因为otherwise内部保存的就是文本节点,也不存在嵌套关系,无需再次处理
            targetContents.add(mixedSqlNode);
        }
    }

    // <choose>标签处理器
    public class ChooseHandler implements NodeHandler {

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 所有的when子节点
            List<SqlNode> whenSqlNodes = new ArrayList<>();
            // 所有的otherwise子节点
            List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
            // 处理when和Otherwise子节点
            this.handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
            // 获取otherwise,并且校验只能有一个otherwise标签
            SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
            // 封装为ChooseSqlNode进行解析
            SqlNode.ChooseSqlNode chooseSqlNode = new SqlNode.ChooseSqlNode(whenSqlNodes, defaultSqlNode);
            // 保存当前节点信息
            targetContents.add(chooseSqlNode);
        }

        // 处理when和otherwise子节点
        public void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
            // 遍历choose的子节点
            List<XNode> children = chooseSqlNode.getChildren();
            for (XNode child : children) {
                // 获取节点名称
                String nodeName = child.getNode().getNodeName();
                // 获取对应节点的处理器
                NodeHandler handler = nodeHandlerMap.get(nodeName);
                // 如果是when节点(if节点和when节点都是使用的IfHandler)
                if (handler instanceof IfHandler) {
                    ((IfHandler) handler).handleNode(child, ifSqlNodes);
                }
                // 如果是otherwise节点
                else if (handler instanceof OtherwiseHandler) {
                    ((OtherwiseHandler) handler).handleNode(child, defaultSqlNodes);
                }
            }
        }

        // 获取otherwise,并且校验只能有一个otherwise标签
        public SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
            SqlNode defaultSqlNode = null;
            if (defaultSqlNodes.size() > 1) {
                throw new BuilderException("Too many default (otherwise) elements in choose statement.");
            }
            if (defaultSqlNodes.size() == 1) {
                defaultSqlNode = defaultSqlNodes.get(0);
            }
            return defaultSqlNode
        }
    }

}

// Mapper.xml语言模板解析器
public interface LanguageDriver {
    // 创建参数处理器,因为需要解析模板中的参数
    ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);

    // 创建SQL的分类,需要解析之后才能判断是动态SQL还是静态SQL
    SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);

    // 创建SQL的分类,需要解析之后才能判断是动态SQL还是静态SQL
    SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);


    // 默认的XML语言的解析器,用于解析SQL模板
    public class XMLLanguageDriver implements LanguageDriver {

        // 创建参数解析器
        @Override
        public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
            return new ParameterHandler.DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
        }

        // 创建SQL的分类信息,该方法是通过xml节点来解析
        @Override
        public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
            // 使用XML脚本的解析器对该SQL脚本进行解析
            XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
            // 解析<insert,select>中的<if>等等动态标签节点
            return builder.parseScriptNode();
        }

        // 创建SQL的分类信息,该方法是提供脚本字符串来解析
        @Override
        public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
            // 如果是以<script>开头,表示是一个SQL动态脚本
            if (script.startsWith("<script>")) {
                // 使用XML解析器解析该脚本字符串
                XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
                // 解析成xml节点
                XNode script = parser.evalNode("/script");
                // 使用XML脚本的解析器对该SQL脚本进行解析
                return this.createSqlSource(configuration, script, parameterType);
            }
            // 不是<script>,就不能写<if>动态标签的SQL
            /**
             * 如果不是以<script>开头,那么使用${}占位符解析器对该脚本进行解析,解析${}内部的变量值,最终将占位符替换实际的变量值
             * 就是从{@link Configuration#variables}属性变量中获取值进行替换,如果开启了org.apache.ibatis.parsing.PropertyParser.enable-default-value=true的全局属性
             * 那么,${}具有提供默认值的功能,例如{id:10},从全局变量中获取id的值,如果不存在,使用默认值10
             * 默认的分割符为":",如果要修改,可以设置全局属性org.apache.ibatis.parsing.PropertyParser.default-value-separator
             */
            script = PropertyParser.parse(script, configuration.variables);
            // 将该SQL脚本封装为TextSqlNode文本节点
            SqlNode.TextSqlNode textSqlNode = new SqlNode.TextSqlNode(script);
            // 是否是动态的SQL,包含${}表示是动态的,否则不是
            if (textSqlNode.isDynamic()) {
                // 返回动态的数据源,需要在执行的过程中进行解析
                return new SqlSource.DynamicSqlSource(configuration, textSqlNode);
            }
            // 如果不是动态的数据源,那么该SQL就是原始的SQL类型,说白了该SQL可以直接使用
            return new SqlSource.RawSqlSource(configuration, script, parameterType);
        }

    }
}

// 全局属性脚本解析器
public class PropertyParser {

    private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
    public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
    public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
    private static final String ENABLE_DEFAULT_VALUE = "false";
    private static final String DEFAULT_VALUE_SEPARATOR = ":";

    /**
     * 解析脚本
     * 就是从{@link Configuration#variables}属性变量中获取值进行替换,如果开启了org.apache.ibatis.parsing.PropertyParser.enable-default-value=true的全局属性
     * 那么,${}具有提供默认值的功能,例如{id:10},从全局变量中获取id的值,如果不存在,使用默认值10
     * 默认的分割符为":",如果要修改,可以设置全局属性org.apache.ibatis.parsing.PropertyParser.default-value-separator
     *
     * @param string    给定的SQL脚本
     * @param variables config对象中的全局属性
     */
    public static String parse(String string, Properties variables) {
        VariableTokenHandler handler = new VariableTokenHandler(variables);
        GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
        return parser.parse(string);
    }

    // 基于变量的占位符解析器
    private static class VariableTokenHandler implements TokenHandler {
        /**
         * 数据源,该数据源是{@link Configuration#variables},是在mybatis-config中配置的属性信息
         */
        private final Properties variables;
        // 是否开启默认值
        private final boolean enableDefaultValue;
        // 默认值的分割符号
        private final String defaultValueSeparator;

        private VariableTokenHandler(Properties variables) {
            this.variables = variables;
            // 是否开启默认值
            // 配置全局属性org.apache.ibatis.parsing.PropertyParser.enable-default-value的值,默认为false
            this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
            // 配置全局属性org.apache.ibatis.parsing.PropertyParser.default-value-separator的值,默认为:
            this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
        }

        // 从数据源中获取
        private String getPropertyValue(String key, String defaultValue) {
            return variables == null ? defaultValue : variables.getProperty(key, defaultValue);
        }

        /**
         * 就是从{@link Configuration#variables}属性变量中获取值进行替换,如果开启了org.apache.ibatis.parsing.PropertyParser.enable-default-value=true的全局属性
         * 那么,${}具有提供默认值的功能,例如{id:10},从全局变量中获取id的值,如果不存在,使用默认值10
         * 默认的分割符为":",如果要修改,可以设置全局属性org.apache.ibatis.parsing.PropertyParser.default-value-separator
         *
         * @param content 占位符中的内容
         */
        @Override
        public String handleToken(String content) {
            // 如果不存在通用的属性变量,不处理,原样将占位符返回
            if (variables != null) {
                String key = content;
                // 是否开启了默认值,默认为false
                if (enableDefaultValue) {
                    // 在解析出来的变量中查找分割符号,默认为:
                    final int separatorIndex = content.indexOf(defaultValueSeparator);
                    String defaultValue = null;
                    // 如果找到了,并且不是以:开头才行
                    if (separatorIndex >= 0) {
                        // ":"前面的作为key
                        key = content.substring(0, separatorIndex);
                        // ":"后面的作为默认值
                        defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
                    }
                    // 如果解析到了默认值
                    if (defaultValue != null) {
                        // 从全局变量中获取,如果全局变量中不存在,则返回表达式提供的默认值
                        return variables.getProperty(key, defaultValue);
                    }
                }
                // 如果未开启默认值模式,那么只会从全局变量中找对应的变量
                if (variables.containsKey(key)) {
                    return variables.getProperty(key);
                }
            }
            // 如果上面没有成功解析该变量,则原样返回
            return "${" + content + "}";
        }
    }

}

// SQL的绑定信息
public class BoundSql {
    // SQL语句
    public final String sql;
    // 需要使用的参数映射信息
    public final List<ParameterMapping> parameterMappings;
    // 参数对象
    public final Object parameterObject;
    // 额外设置的自定义参数
    public final Map<String, Object> additionalParameters;
    // 参数集合的元数据信息对象
    public final MetaObject metaParameters;

    public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
        this.sql = sql;
        this.parameterMappings = parameterMappings;
        this.parameterObject = parameterObject;
        this.additionalParameters = new HashMap<>();
        this.metaParameters = configuration.newMetaObject(additionalParameters);
    }

    // 是否存在自定义参数
    public boolean hasAdditionalParameter(String name) {
        String paramName = new PropertyTokenizer(name).getName();
        return additionalParameters.containsKey(paramName);
    }

    // 添加自定义参数
    public void setAdditionalParameter(String name, Object value) {
        metaParameters.setValue(name, value);
    }

    // 获取自定义参数的值
    public Object getAdditionalParameter(String name) {
        return metaParameters.getValue(name);
    }

    // 获取所有的自定义参数
    public Map<String, Object> getAdditionalParameters() {
        return additionalParameters;
    }
}

// SQL分类的构建器,用于解析SQL
public class SqlSourceBuilder extends BaseBuilder {
    // 参数属性
    public static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";

    public SqlSourceBuilder(Configuration configuration) {
        super(configuration);
    }

    // 解析SQL,将#{}替换为?
    public SqlSource.StaticSqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
        // 参数映射的占位符解析器
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
        // 使用#{}类型的占位符解析器解析参数
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        String sql;
        if (configuration.isShrinkWhitespacesInSql()) {
            // 删除多余的空格
            sql = removeExtraWhitespaces(originalSql);
            // 使用#{}类型的占位符解析器解析SQL
            sql = parser.parse(sql);
        } else {
            // 使用#{}类型的占位符解析器解析SQL
            sql = parser.parse(originalSql);
        }
        // 此时SQL已经解析好了,因此返回静态的SQL分类
        return new SqlSource.StaticSqlSource(configuration, sql, handler.getParameterMappings());
    }

    // 删除多余的空格
    public static String removeExtraWhitespaces(String original) {
        StringTokenizer tokenizer = new StringTokenizer(original);
        StringBuilder builder = new StringBuilder();
        boolean hasMoreTokens = tokenizer.hasMoreTokens();
        while (hasMoreTokens) {
            builder.append(tokenizer.nextToken());
            hasMoreTokens = tokenizer.hasMoreTokens();
            if (hasMoreTokens) {
                builder.append(' ');
            }
        }
        return builder.toString();
    }

    // 参数映射的占位符解析器
    public static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {

        // 该SQL解析出来的参数的映射信息
        // 例如  username=#{username,jdbcType=VARCHAR},就要构建 属性名为username,值为参数对象的username值的映射关系
        public final List<ParameterMapping> parameterMappings = new ArrayList<>();
        // 参数类型
        public final Class<?> parameterType;
        // 解析SQL过程中提供的参数
        public final MetaObject metaParameters;

        public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
            super(configuration);
            this.parameterType = parameterType;
            this.metaParameters = configuration.newMetaObject(additionalParameters);
        }

        public List<ParameterMapping> getParameterMappings() {
            return parameterMappings;
        }

        /**
         * 处理占位符
         *
         * @param content 为占位符内部的内容
         * @return 处理占位符之后的结果
         */
        @Override
        public String handleToken(String content) {
            // 自动根据占位符来构建对应的参数映射信息
            // 例如 id = #{id},就要构建 属性名为id,值为参数对象的Id值的映射关系
            ParameterMapping mapping = this.buildParameterMapping(content);
            // 保存该参数的映射信息
            parameterMappings.add(mapping);
            // 将占位符使用?替代,也就是将#{id} = ? 替代
            return "?";
        }

        // 自动根据占位符来构建对应的参数映射信息
        // 例如 id = #{id},就要构建 属性名为id,值为参数对象的Id值的映射关系
        public ParameterMapping buildParameterMapping(String content) {
            // 解析sql中的#{}内部的属性信息
            // 例如 username=#{username,jdbcType=VARCHAR}
            // 默认第一个","分隔符之前的会使用固定的key:property,value为username保存
            // 之后就使用对应的键值对保存
            Map<String, String> propertiesMap = this.parseParameterMapping(content);
            // 获取解析到的参数名称
            String property = propertiesMap.get("property");
            // 要获取该属性的java类型
            Class<?> propertyType;
            if (metaParameters.hasGetter(property)) {
                propertyType = metaParameters.getGetterType(property);
            } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
                propertyType = parameterType;
            } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
                propertyType = java.sql.ResultSet.class;
            } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
                propertyType = Object.class;
            } else {
                MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
                if (metaClass.hasGetter(property)) {
                    propertyType = metaClass.getGetterType(property);
                } else {
                    propertyType = Object.class;
                }
            }
            // 构建参数映射信息
            ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
            //
            Class<?> javaType = propertyType;
            String typeHandlerAlias = null;
            // 遍历解析到占位符内的所有键值对
            // 例如 username=#{username,jdbcType=VARCHAR}
            for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
                // 根据不同的属性来设置不同的映射关系
                String name = entry.getKey();
                String value = entry.getValue();
                if ("javaType".equals(name)) {
                    javaType = resolveClass(value);
                    builder.javaType(javaType);
                } else if ("jdbcType".equals(name)) {
                    builder.jdbcType(resolveJdbcType(value));
                } else if ("mode".equals(name)) {
                    builder.mode(resolveParameterMode(value));
                } else if ("numericScale".equals(name)) {
                    builder.numericScale(Integer.valueOf(value));
                } else if ("resultMap".equals(name)) {
                    builder.resultMapId(value);
                } else if ("typeHandler".equals(name)) {
                    typeHandlerAlias = value;
                } else if ("jdbcTypeName".equals(name)) {
                    builder.jdbcTypeName(value);
                } else if ("property".equals(name)) {
                } else if ("expression".equals(name)) {
                    throw new BuilderException("Expression based parameters are not supported yet");
                } else {
                    throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + PARAMETER_PROPERTIES);
                }
            }
            // 如果指定了类型转换器
            if (typeHandlerAlias != null) {
                builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
            }
            // 通过以上信息构建参数映射信息
            return builder.build();
        }

    }

}


// 用于描述SQL语句分类信息
public interface SqlSource {

    // 获取绑定的SQL
    BoundSql getBoundSql(Object parameterObject);

    // 表示当前SQL是一个动态SQL,需要在执行的过程进行解析
    public static class DynamicSqlSource implements SqlSource {
        // 配置类
        public Configuration configuration;
        /**
         * SQL节点,因为它是动态SQL,只能保存节点信息,在执行的过程中对该节点进行解析
         * 实际上是{@link SqlNode.MixedSqlNode}类型,内部包含了该映射语句中的所有动态标签节点
         */
        public SqlNode.MixedSqlNode rootSqlNode;

        // 在运行的时候进行调用
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            // 创建动态执行的上下文对象
            DynamicContext context = new DynamicContext(configuration, parameterObject);
            // 处理动态SQL标签,执行所有动态SQL标签的解析方法,因为这些标签需要根据运行时的参数进行表达式计算,这运行的过程中执行就能成功的完成解析
            rootSqlNode.apply(context);
            // 创建SQL解析器,用于解析#{}
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            // 获取到方法的参数的java类型
            Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
            // 解析SQL中的#{}占位符替换为?,得到一个静态的Sql分类,表示SQL已经解析好了
            SqlSource.StaticSqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
            // 将SQL信息封装为BoundSQL对象
            BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
            // 将解析过程中保存到上下文的参数对象都设置到boundSQL中,因为SQL中的占位符需要的参数就可能在这里面,到时候设置参数就可以直接从boundSql的参数对象中获取
            context.getBindings().forEach(boundSql::setAdditionalParameter);
            return boundSql;
        }
    }

    // 静态的SQL分类,保存的就是可以使用的SQL
    public static class StaticSqlSource implements SqlSource {
        // SQL语句
        public String sql;
        // 参数映射信息
        public List<ParameterMapping> parameterMappings;
        // 配置类
        public Configuration configuration;

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return new BoundSql(configuration, sql, parameterMappings, parameterObject);
        }
    }

    // 原始的SQL分类,它也是静态SQL分类的一种,该SQL分类表示可以一个直接能使用的SQL或者具有#{}预编译占位符的SQL
    public class RawSqlSource implements SqlSource {
        /**
         * 实际上就是静态SQL分类为{@link SqlSource.StaticSqlSource}
         */
        public final SqlSource.StaticSqlSource sqlSource;

        /**
         * 通过XML节点解析到的SQL类别
         *
         * @param configuration 配置信息
         * @param rootSqlNode   <select>中所有子节点,包括<if>等动态标签
         * @param parameterType
         */
        public RawSqlSource(Configuration configuration, SqlNode.MixedSqlNode rootSqlNode, Class<?> parameterType) {
            // 解析动态SQL节点<if>等等
            String sql = this.getSql(configuration, rootSqlNode);
            this(configuration, sql, parameterType);
        }

        // 通过字符串脚本解析到的SQL类别
        public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
            // 创建SQL解析器,用于解析#{}
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            Class<?> clazz = parameterType == null ? Object.class : parameterType;
            // 解析SQL,将#{}替换为?
            sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
        }

        // 解析动态SQL节点<if>等等
        public static String getSql(Configuration configuration, SqlNode rootSqlNode) {
            // 创建动态SQL的上下文
            DynamicContext context = new DynamicContext(configuration, null);
            // 解析动态SQL解析
            rootSqlNode.apply(context);
            // 获取解析后的SQL
            return context.getSql();
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return sqlSource.getBoundSql(parameterObject);
        }

    }

    // 使用@SqlProvider这种注解提供的SQL分类
    public static class ProviderSqlSource implements SqlSource {
        // 配置类
        public Configuration configuration;
        // 提供SQL的类
        public Class<?> providerType;
        // 语言驱动,解析模板的
        public LanguageDriver languageDriver;
        // Mapper接口中的方法
        public Method mapperMethod;
        // providerType中提供生成Sql的方法对象
        public Method providerMethod;
        // providerMethod方法的参数名称
        public String[] providerMethodArgumentNames;
        // providerMethod方法的参数类型名称
        public Class<?>[] providerMethodParameterTypes;
        // 处理的上下文对象
        public ProviderContext providerContext;
        // 处理的上下文对象的索引
        public Integer providerContextIndex;

        /**
         * 使用@SqlProvider,可以提供type属性,该类用来提供执行的SQL,如果没有设置指定的method属性,则默认生成SQL的方法为provideSql
         * 如果存在多个返回值为String,方法名相同的多个方法,则会抛出异常,不会校验参数
         */
        public ProviderSqlSource(Configuration configuration, Annotation provider, Class<?> mapperType, Method mapperMethod) {
            // 提供SQL的方法名称
            String candidateProviderMethodName;
            // 提供SQL的方法对象
            Method candidateProviderMethod = null;
            this.configuration = configuration;
            this.mapperMethod = mapperMethod;
            // 获取Mapper接口中方法上的@Lang注解
            Lang lang = mapperMethod == null ? null : mapperMethod.getAnnotation(Lang.class);
            // 使用注解指定的语言模版解析器
            this.languageDriver = configuration.getLanguageDriver(lang == null ? null : lang.value());
            // 获取sqlProvider注解中的类型
            this.providerType = this.getProviderType(configuration, provider, mapperMethod);
            // 获取@SqlProvider的method方法的值
            candidateProviderMethodName = (String) provider.annotationType().getMethod("method").invoke(provider);

            // 如果并没有提供方法,而提供类型是ProviderMethodResolver,该接口能返回一个方法对象
            if (candidateProviderMethodName.length() == 0 && ProviderMethodResolver.class.isAssignableFrom(this.providerType)) {
                // 调用该ProviderMethodResolver接口实现的resolveMethod方法,得到一个指定的提供sql的方法对象
                candidateProviderMethod = ((ProviderMethodResolver) this.providerType.getDeclaredConstructor().newInstance()).resolveMethod(new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId()));
            }
            // 如果提供SQL的类型不是ProviderMethodResolver的时候或者ProviderMethodResolver没有提供方法对象
            if (candidateProviderMethod == null) {
                // 如果没有设置method属性,则默认的方法名为provideSql,如果提供了,则为指定的方法名
                candidateProviderMethodName = candidateProviderMethodName.length() == 0 ? "provideSql" : candidateProviderMethodName;
                // 在指定提供SQL的类中找到与之匹配的方法名,并且返回值为String的方法对象,
                for (Method m : this.providerType.getMethods()) {
                    if (candidateProviderMethodName.equals(m.getName()) && CharSequence.class.isAssignableFrom(m.getReturnType())) {
                        // 如果不为空,表示之前找到了与该方法同名的提供SQL的方法,抛出异常
                        if (candidateProviderMethod != null) {
                            throw new BuilderException("Error creating SqlSource for SqlProvider. Method '" + candidateProviderMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName() + "'. Sql provider method can not overload.");
                        }
                        // 保存该提供SQL的方法
                        candidateProviderMethod = m;
                    }
                }
            }
            // 提供SQL的方法对象
            this.providerMethod = candidateProviderMethod;
            // 找到提供SQL的方法的参数名称,存在@Param注解,使用注解的名称,否则使用方法参数的实际名称
            this.providerMethodArgumentNames = new ParamNameResolver(configuration, this.providerMethod).getNames();
            // 找到提供SQL的方法的参数的类型
            this.providerMethodParameterTypes = this.providerMethod.getParameterTypes();
            // 提供SQL的上下文对象
            ProviderContext candidateProviderContext = null;
            // 提供SQL的上下文对象的参数索引
            Integer candidateProviderContextIndex = null;
            // 遍历所有的参数
            for (int i = 0; i < this.providerMethodParameterTypes.length; i++) {
                Class<?> parameterType = this.providerMethodParameterTypes[i];
                // 如果提供SQL的方法的参数为ProviderContext
                if (parameterType == ProviderContext.class) {
                    // 校验方法是否存在多个ProviderContext的参数,如果存在多个,抛出异常
                    if (candidateProviderContext != null) {
                        throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method (" + this.providerType.getName() + "." + providerMethod.getName() + "). ProviderContext can not define multiple in SqlProvider method argument.");
                    }
                    // 提供SQL的上下文对象
                    candidateProviderContext = new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId());
                    // 保存提供SQL的上下文对象的参数索引
                    candidateProviderContextIndex = i;
                }
            }
            // 提供SQL的上下文对象
            this.providerContext = candidateProviderContext;
            // 提供SQL的上下文对象的参数索引
            this.providerContextIndex = candidateProviderContextIndex;
        }

        // 获取到注解中提供SQL的类
        public Class<?> getProviderType(Configuration configuration, Annotation providerAnnotation, Method mapperMethod) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 获取注解中的type/value属性
            Class<?> type = (Class<?>) providerAnnotation.annotationType().getMethod("type").invoke(providerAnnotation);
            Class<?> value = (Class<?>) providerAnnotation.annotationType().getMethod("value").invoke(providerAnnotation);
            // 如果是默认的void类型
            if (value == void.class && type == void.class) {
                // 如果设置了默认的sqlProvider
                if (configuration.getDefaultSqlProviderType() != null) {
                    // 使用默认的sqlProvider
                    return configuration.getDefaultSqlProviderType();
                }
                // 如果存在@SqlProvider注解,但是没有设置提供SQL的类,则抛出异常
                throw new BuilderException("Please specify either 'value' or 'type' attribute of @" + providerAnnotation.annotationType().getSimpleName() + " at the '" + mapperMethod.toString() + "'.");

            }
            // 同时设置了value和type,也抛出异常
            if (value != void.class && type != void.class && value != type) {
                throw new BuilderException("Cannot specify different class on 'value' and 'type' attribute of @" + providerAnnotation.annotationType().getSimpleName() + " at the '" + mapperMethod.toString() + "'.");
            }
            // 返回注解中的其中一个属性设置的类
            return value == void.class ? type : value;
        }
    }
}

// 动态SQL的上下文
public class DynamicContext {

    // 参数对象的KEY
    public static final String PARAMETER_OBJECT_KEY = "_parameter";
    // 数据库Id的KEY
    public static final String DATABASE_ID_KEY = "_databaseId";

    static {
        // OGNL表达式解析器
        OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
    }

    // 在整个解析过程中绑定的属性信息
    public final ContextMap bindings;
    // SQL拼接器
    public final StringJoiner sqlBuilder = new StringJoiner(" ");
    // 提供一个唯一的数字
    public int uniqueNumber;

    public DynamicContext(Configuration configuration, Object parameterObject) {
        // 如果存在参数,但是参数不是Map
        if (parameterObject != null && !(parameterObject instanceof Map)) {
            // 将该对象封装成对象的元数据信息
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            // 参数对象是否存在类型转换器,如果存在,那么到时候获取参数对象中的某个属性值时候,当那个属性值没有提供get方法的时候,将会直接返回整个参数对象作为指定属性的值
            boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
            // 创建在整个解析过程中绑定的属性信息
            bindings = new ContextMap(metaObject, existsTypeHandler);
        }
        // 如果是Map
        else {
            bindings = new ContextMap(null, false);
        }
        // 初始化两个默认的属性
        // 1. 参数对象在解析的过程中一直存在
        bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
        // 2. 数据库ID在解析的过程中一直存在
        bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
    }

    public Map<String, Object> getBindings() {
        return bindings;
    }

    // 添加绑定字段信息
    public void bind(String name, Object value) {
        bindings.put(name, value);
    }

    // 拼接SQL
    public void appendSql(String sql) {
        sqlBuilder.add(sql);
    }

    // 获取解析好的SQL语句
    public String getSql() {
        return sqlBuilder.toString().trim();
    }

    public int getUniqueNumber() {
        return uniqueNumber++;
    }

    // 在整个解析过程中绑定的属性信息
    public static class ContextMap extends HashMap<String, Object> {
        // 参数对象
        public final MetaObject parameterMetaObject;
        // 是否是都兜底的参数对象
        public final boolean fallbackParameterObject;

        public ContextMap(MetaObject parameterMetaObject, boolean fallbackParameterObject) {
            this.parameterMetaObject = parameterMetaObject;
            this.fallbackParameterObject = fallbackParameterObject;
        }

        @Override
        public Object get(Object key) {
            // 获取绑定的属性值
            String strKey = (String) key;
            if (super.containsKey(strKey)) {
                return super.get(strKey);
            }
            // 如果不存在参数对象,返回null
            if (parameterMetaObject == null) {
                return null;
            }
            // 如果开启了兜底的参数模式,并且该参数没有get方法,此时直接返回整个参数对象,并不会返回指定参数的值
            if (fallbackParameterObject && !parameterMetaObject.hasGetter(strKey)) {
                return parameterMetaObject.getOriginalObject();
            }
            // 直接从参数对象中获取属性值
            return parameterMetaObject.getValue(strKey);
        }
    }
}

// 动态SQL标签
public interface SqlNode {
    // 处理动态SQL标签
    boolean apply(DynamicContext context);

    // <if>动态标签
    public class IfSqlNode implements SqlNode {
        // 表单式计算器
        public final ExpressionEvaluator evaluator;
        // if表达式
        public final String test;
        // <if>的子标签,因为可能存在嵌套if,或者SQL语句(也会当做一个SQL节点)
        public final SqlNode contents;

        @Override
        public boolean apply(DynamicContext context) {
            // 解析OGNL表达式
            // 如果OGNL符合条件
            if (evaluator.evaluateBoolean(test, context.getBindings())) {
                // 执行该SQL中所有子节点的解析逻辑
                contents.apply(context);
                // 符合OGNL条件
                return true;
            }
            // 如果OGNL表达式不符合,返回false
            return false;
        }

    }

    // <choose>动态标签
    public class ChooseSqlNode implements SqlNode {
        // otherwise节点信息
        private final SqlNode defaultSqlNode;
        // when节点信息
        private final List<IfSqlNode> ifSqlNodes;

        @Override
        public boolean apply(DynamicContext context) {
            // 执行所有when节点的
            for (IfSqlNode sqlNode : ifSqlNodes) {
                // 只要有一个when符合条件就返回
                if (sqlNode.apply(context)) {
                    return true;
                }
            }
            // 如果when都没有满足,解析otherwise内部的节点,内部又递归解析子标签
            if (defaultSqlNode != null) {
                defaultSqlNode.apply(context);
                return true;
            }
            return false;
        }
    }

    // <bind>变量定义的SQL节点
    public class VarDeclSqlNode implements SqlNode {
        // 变量名称
        public final String name;
        // 变量表达式
        public final String expression;

        public VarDeclSqlNode(String name, String exp) {
            this.name = name;
            this.expression = exp;
        }

        @Override
        public boolean apply(DynamicContext context) {
            // 解析OGNL表达式,获取表达式最终的值
            final Object value = OgnlCache.getValue(expression, context.getBindings());
            // 将该变量和值绑定到动态执行的上下文中,同一个上下文可以共享这个变量
            context.bind(name, value);
            return true;
        }

    }


    /**
     * <pre>
     *      <trim prefix="WHERE" prefixOverrides="AND | OR ">
     *            <if test="state != null">
     *                 state = #{state}
     *            </if>
     *            <if test="title != null">
     *                AND title like #{title}
     *            </if>
     *      </trim>
     * </pre>
     */
    // <trim>标签节点
    public class TrimSqlNode implements SqlNode {
        // 内部的所有子节点
        public final SqlNode contents;
        // 需要添加的前缀
        public final String prefix;
        // 需要添加的后缀
        public final String suffix;
        // 需要删除的前缀
        public final List<String> prefixesToOverride;
        // 需要删除的后缀
        public final List<String> suffixesToOverride;
        // 配置信息
        public final Configuration configuration;

        public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) {
            this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));
        }

        protected TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, List<String> prefixesToOverride, String suffix, List<String> suffixesToOverride) {
            this.contents = contents;
            this.prefix = prefix;
            this.prefixesToOverride = prefixesToOverride;
            this.suffix = suffix;
            this.suffixesToOverride = suffixesToOverride;
            this.configuration = configuration;
        }

        // 解析节点
        @Override
        public boolean apply(DynamicContext context) {
            // 创建带有过滤的动态上下文
            FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
            /**
             * 执行所有子节点的SQL解析方法
             * {@link SqlNode.MixedSqlNode#apply(DynamicContext)}
             */
            boolean result = contents.apply(filteredDynamicContext);
            // 将解析的SQL进行过滤,过滤多余的前缀或后缀
            filteredDynamicContext.applyAll();
            return result;
        }

        // 解析Overrides表达式,例如 <trim prefix="WHERE" prefixOverrides="AND | OR ">,需要保存AND和OR
        public static List<String> parseOverrides(String overrides) {
            if (overrides != null) {
                final StringTokenizer parser = new StringTokenizer(overrides, "|", false);
                final List<String> list = new ArrayList<>(parser.countTokens());
                while (parser.hasMoreTokens()) {
                    list.add(parser.nextToken().toUpperCase(Locale.ENGLISH));
                }
                return list;
            }
            return Collections.emptyList();
        }

        // 具有过滤功能的动态SQL上下文
        public class FilteredDynamicContext extends DynamicContext {
            // 代理的上下文
            public final DynamicContext delegate;
            // 是否已经添加过前缀
            public boolean prefixApplied;
            // 是否已经添加过后缀
            public boolean suffixApplied;
            // SqlNode解析的节点,会被当前上下文拦截,在SqlNode解析的时候,会调用appendSql保存到这里
            // 最终的SQL是保存到delegate中,当前上下文拦截之后,就可以对这些SQL进行修改,修改之后再放回到原来的上下文中
            public StringBuilder sqlBuffer;

            // 执行过滤SQL
            public void applyAll() {
                // 保存原来的SQL
                sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
                // 全部转换为小写
                String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
                // 如果存在SQL
                if (trimmedUppercaseSql.length() > 0) {
                    // 添加前缀操作
                    applyPrefix(sqlBuffer, trimmedUppercaseSql);
                    // 添加后缀操作
                    applySuffix(sqlBuffer, trimmedUppercaseSql);
                }
                // 将过滤好的sql真正的添加到动态上下文中
                delegate.appendSql(sqlBuffer.toString());
            }

            @Override
            public void appendSql(String sql) {
                sqlBuffer.append(sql);
            }

            // 执行前缀的添加和删除
            public void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
                // 第一次执行,默认为false
                if (!prefixApplied) {
                    // 标记为true
                    prefixApplied = true;
                    // 如果需要删除前缀
                    if (prefixesToOverride != null) {
                        // 执行删除前缀操作
                        for (String toRemove : prefixesToOverride) {
                            if (trimmedUppercaseSql.startsWith(toRemove)) {
                                sql.delete(0, toRemove.trim().length());
                                break;
                            }
                        }
                    }
                    // 如果需要添加前缀
                    if (prefix != null) {
                        // 添加空格,再添加前缀
                        sql.insert(0, " ");
                        sql.insert(0, prefix);
                    }
                }
            }

            // 执行后缀的添加和删除
            public void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
                // 第一次执行,默认为false
                if (!suffixApplied) {
                    // 标记为true
                    suffixApplied = true;
                    // 如果需要删除后缀
                    if (suffixesToOverride != null) {
                        // 执行删除后缀操作
                        for (String toRemove : suffixesToOverride) {
                            if (trimmedUppercaseSql.endsWith(toRemove) || trimmedUppercaseSql.endsWith(toRemove.trim())) {
                                int start = sql.length() - toRemove.trim().length();
                                int end = sql.length();
                                sql.delete(start, end);
                                break;
                            }
                        }
                    }// 如果需要添加后缀
                    if (suffix != null) {
                        // 添加空格,再添加后缀
                        sql.append(" ");
                        sql.append(suffix);
                    }
                }
            }

        }

    }

    // <where>标签节点,就是借助了<trim>完成
    public class WhereSqlNode extends TrimSqlNode {

        private static final List<String> prefixList = Arrays.asList("AND ", "OR ", "AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t");

        // 会自动添加WHERE前缀和删除AND|OR前缀
        public WhereSqlNode(Configuration configuration, SqlNode contents) {
            super(configuration, contents, "WHERE", prefixList, null, null);
        }

    }

    // <set>标签节点,就是借助了<trim>完成
    public class SetSqlNode extends TrimSqlNode {

        private static final List<String> COMMA = Collections.singletonList(",");

        // 会自动添加SET前缀和删除","前缀
        public SetSqlNode(Configuration configuration, SqlNode contents) {
            super(configuration, contents, "SET", COMMA, null, COMMA);
        }

    }

    // <foreach>标签节点
    public class ForEachSqlNode implements SqlNode {
        public static final String ITEM_PREFIX = "__frch_";

        // 表达式计算器
        private final ExpressionEvaluator evaluator;
        /**
         * 例如:
         * <pre>
         *     <select id="findByIds">
         *         select *
         *         from users
         *         where id in
         *         <foreach collection="ids" item="id" open="(" close=")" separator="," nullable="false">
         *             #{id}
         *         </foreach>
         *     </select>
         * </pre>
         */
        // 表示变量集合的名称
        private final String collectionExpression;
        // 表示给定的集合是否可以为空,默认为false,为空抛出异常
        private final Boolean nullable;
        // <foreach>所有的子节点
        private final MixedSqlNode contents;
        // 表示循环之前需要拼接的前缀
        private final String open;
        // 表示循环之后需要拼接的前缀
        private final String close;
        // 每一个元素之间的分割符号
        private final String separator;
        // 元素的变量名
        private final String item;
        // 元素的索引名称
        private final String index;
        // 配置信息
        private final Configuration configuration;


        /**
         * 解析标签
         * <pre>
         *     <select id="findByIds">
         *         select *
         *         from users
         *         where id in
         *         <foreach collection="ids" item="id" open="(" close=")" separator=",">
         *             #{id}
         *         </foreach>
         *     </select>
         * </pre>
         */
        @Override
        public boolean apply(DynamicContext context) {
            //
            /**
             * 从上下文中获取已经绑定的参数变量信息
             *
             * 从{@link DynamicContext#DynamicContext}上下对象中,将参数到bindinds对象中了
             * 而对于执行参数是集合的时候,{@link ParamNameResolver#wrapToMapIfCollection}会自动将参数转换为Map进行执行,并且会添加额外的参数
             */
            Map<String, Object> bindings = context.getBindings();
            // 获取到执行的参数对象
            final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings, Optional.ofNullable(nullable).orElseGet(configuration.nullableOnForEach));
            // 如果不存在参数或者参数没有数据,不往下处理
            if (iterable == null || !iterable.iterator().hasNext()) {
                return true;
            }
            boolean first = true;
            // 添加open字符串到SQL中
            applyOpen(context);
            int i = 0;
            // 遍历所有的数据
            for (Object o : iterable) {
                // 保存原来的上下文
                DynamicContext oldContext = context;
                // 第一次执行或者没有设置分割符号
                if (first || separator == null) {
                    // 创建一个不需要拼接前缀的上下文对象
                    context = new PrefixedContext(context, "");
                } else {
                    // 其他情况都需要拼接分割符号
                    context = new PrefixedContext(context, separator);
                }
                int uniqueNumber = context.getUniqueNumber();
                // 如果集合中的对象是一个map
                if (o instanceof Map.Entry) {
                    Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
                    // 绑定index变量
                    applyIndex(context, mapEntry.getKey(), uniqueNumber);
                    // 绑定item元素变量
                    applyItem(context, mapEntry.getValue(), uniqueNumber);
                } else {
                    // 其他对象
                    // 绑定index变量
                    applyIndex(context, i, uniqueNumber);
                    // 绑定item元素变量
                    applyItem(context, o, uniqueNumber);
                }
                // 执行所有子节点的SQL解析操作,并且会通过FilteredDynamicContext上下文对象将SQL进行改写
                contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
                if (first) {
                    // 如果没有添加过前缀,first就一直为true
                    first = !((PrefixedContext) context).isPrefixApplied();
                }
                // 还原原来的上下文对象
                context = oldContext;
                i++;
            }
            // 拼接后缀
            applyClose(context);
            // 删除绑定的item和index变量值
            context.getBindings().remove(item);
            context.getBindings().remove(index);
            return true;
        }

        // 绑定index变量,会绑定用户自己提供的index变量和mybatis自己生成的变量
        private void applyIndex(DynamicContext context, Object o, int i) {
            if (index != null) {
                context.bind(index, o);
                context.bind(itemizeItem(index, i), o);
            }
        }

        // 绑定item变量,会绑定用户自己提供的item变量和mybatis自己生成的变量
        private void applyItem(DynamicContext context, Object o, int i) {
            if (item != null) {
                context.bind(item, o);
                context.bind(itemizeItem(item, i), o);
            }
        }

        private void applyOpen(DynamicContext context) {
            if (open != null) {
                context.appendSql(open);
            }
        }

        private void applyClose(DynamicContext context) {
            if (close != null) {
                context.appendSql(close);
            }
        }

        // 将指定变量与元素的索引进行拼接
        // #{__frch_id_0}
        // #{__frch_id_1}
        // #{__frch_id_N}
        private static String itemizeItem(String item, int i) {
            return ITEM_PREFIX + item + "_" + i;
        }

        // 具有过滤功能的上下文对象,因为最终foreach需要对标签内的SQL进行改写
        private static class FilteredDynamicContext extends DynamicContext {
            // 实际保存SQL的上下文对象
            private final DynamicContext delegate;
            // 解析第几个变量
            private final int index;
            // 正在解析的标签中索引的名称: idx <foreach index="idx" >
            private final String itemIndex;
            // 正在解析的变量名: id  <foreach index="idx" > #{id} </foreach>
            private final String item;

            @Override
            public Map<String, Object> getBindings() {
                return delegate.getBindings();
            }

            @Override
            public void bind(String name, Object value) {
                delegate.bind(name, value);
            }

            @Override
            public String getSql() {
                return delegate.getSql();
            }

            // 拦截拼接sql方法,处理之后再将sql保存到实际的上下文对象中
            @Override
            public void appendSql(String sql) {
                // 使用#{}占位符解析器解析,如果解析到#{}内部的变量
                GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> {
                    // content为解析#{}到的内部的变量名
                    // 将指定变量与元素的索引进行拼接,变成以下格式
                    // #{__frch_id_0}
                    // #{__frch_id_1}
                    // #{__frch_id_N}
                    String newContent = content.replaceFirst("^\\s*" + item + "(?![^.,:\\s])", itemizeItem(item, index));
                    if (itemIndex != null && newContent.equals(content)) {
                        newContent = content.replaceFirst("^\\s*" + itemIndex + "(?![^.,:\\s])", itemizeItem(itemIndex, index));
                    }
                    //
                    return "#{" + newContent + "}";
                });
                // 使用占位符解析器开始解析
                sql = parser.parse(sql);
                // 最终解析好的SQL为  select * from users where id in ( #{__frch_id_0},#{__frch_id_1},#{__frch_id_n})
                delegate.appendSql(sql);
            }

            @Override
            public int getUniqueNumber() {
                return delegate.getUniqueNumber();
            }

        }

        // 用于处理前缀的上下文对象
        private class PrefixedContext extends DynamicContext {
            // 使用保存SQL的上下文
            private final DynamicContext delegate;
            // 需要添加的前缀
            private final String prefix;
            // 是否已经添加过前缀
            private boolean prefixApplied;

            public PrefixedContext(DynamicContext delegate, String prefix) {
                super(configuration, null);
                this.delegate = delegate;
                this.prefix = prefix;
                this.prefixApplied = false;
            }

            public boolean isPrefixApplied() {
                return prefixApplied;
            }

            @Override
            public Map<String, Object> getBindings() {
                return delegate.getBindings();
            }

            @Override
            public void bind(String name, Object value) {
                delegate.bind(name, value);
            }

            // 拦截拼接sql方法,处理之后再将sql保存到实际的上下文对象中
            @Override
            public void appendSql(String sql) {
                // 如果未添加过前缀,并且存在SQL
                if (!prefixApplied && sql != null && sql.trim().length() > 0) {
                    // 将该SQL添加前缀
                    delegate.appendSql(prefix);
                    // 标记为已经添加过前缀
                    prefixApplied = true;
                }
                // 将加工后的SQL保存到原始上下文中
                delegate.appendSql(sql);
            }

            @Override
            public String getSql() {
                return delegate.getSql();
            }

            @Override
            public int getUniqueNumber() {
                return delegate.getUniqueNumber();
            }
        }

    }


    // 混合的SQL标签
    public class MixedSqlNode implements SqlNode {
        // 混合的所有SQL标签
        public final List<SqlNode> contents;

        public MixedSqlNode(List<SqlNode> contents) {
            this.contents = contents;
        }

        @Override
        public boolean apply(DynamicContext context) {
            // 执行所有的SQL标签
            contents.forEach(node -> node.apply(context));
            return true;
        }
    }

    // 静态的SQL节点,该节点保存的SQL可以直接使用
    public class StaticTextSqlNode implements SqlNode {
        public final String text;

        public StaticTextSqlNode(String text) {
            this.text = text;
        }

        // 直接将该SQL进行拼接
        @Override
        public boolean apply(DynamicContext context) {
            context.appendSql(text);
            return true;
        }

    }

    // XML中的文本节点,就是在XML中写的一个具体的SQL,没有经过处理的原样SQL
    // 例如 select * from users where id = ${id} 或者 select * from users where username=#{username,jdbcType=VARCHAR} and password=#{password}
    public class TextSqlNode implements SqlNode {
        // SQL语句
        public final String text;
        // 过滤器
        public final Pattern injectionFilter;

        // 是否是动态的SQL,包含${}表示是动态的,否则不是
        public boolean isDynamic() {
            DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
            // 创建${}占位符解析器
            GenericTokenParser parser = createParser(checker);
            // 解析节点
            parser.parse(text);
            // 如果SQL中包含${},就会调用DynamicCheckerTokenParser处理逻辑,会将动态的标识设置为true,表示是动态的,否则不是
            return checker.isDynamic();
        }

        // 解析当前SQL节点
        @Override
        public boolean apply(DynamicContext context) {
            GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
            context.appendSql(parser.parse(text));
            return true;
        }

        // 普通的占位符解析器,为${}
        public GenericTokenParser createParser(TokenHandler handler) {
            return new GenericTokenParser("${", "}", handler);
        }

        // 动态检查的占位符解析器
        public static class DynamicCheckerTokenParser implements TokenHandler {

            public boolean isDynamic;

            public boolean isDynamic() {
                return isDynamic;
            }

            @Override
            public String handleToken(String content) {
                // 只要该类处理,那么就是动态的
                this.isDynamic = true;
                return null;
            }
        }
    }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值