Mybatis学习[2] Mybatis中的设计模式之策略模式

993 阅读1分钟

1.策略模式详解

👉 看这篇文章就够了,令人幸福XD

2.Mybatis中的策略模式

我们先来康康策略模式的类图:

https://gitee.com/ambitionHuang/my-article-images/raw/master/mybatis/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200822143214.png

一共有三个部分:1.策略的接口;2.实现策略接口的具体实现类;3.持有策略实例的具体上下文对象。

在mybatis中使用到上述类图这种结构的策略模式的地方还是挺多的,比如说DefaultSqlSession和GenericTokenParser等。在这里我们就以GenericTokenParser来举例子。

GenericTokenParser是mybatis中的通用变量(占位符)解析器,比如Mapper.xml中sql的入参变量,mybatis-config.xml中的配置变量(property)等,都是通过GenericTokenParser来解析的,下面我们来看下GenericTokenParser的源码:

package org.apache.ibatis.parsing;

public class GenericTokenParser {
    private final String openToken;
    private final String closeToken;
    private final TokenHandler handler;

    public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
        this.openToken = openToken;
        this.closeToken = closeToken;
        this.handler = handler;
    }

    public String parse(String text) {
        if (text != null && !text.isEmpty()) {
            int start = text.indexOf(this.openToken);
            if (start == -1) {
                return text;
            } else {
                char[] src = text.toCharArray();
                int offset = 0;
                StringBuilder builder = new StringBuilder();

                for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
                    if (start > 0 && src[start - 1] == '\\') {
                        builder.append(src, offset, start - offset - 1).append(this.openToken);
                        offset = start + this.openToken.length();
                    } else {
                        if (expression == null) {
                            expression = new StringBuilder();
                        } else {
                            expression.setLength(0);
                        }

                        builder.append(src, offset, start - offset);
                        offset = start + this.openToken.length();

                        int end;
                        for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
                            if (end <= offset || src[end - 1] != '\\') {
                                expression.append(src, offset, end - offset);
                                break;
                            }

                            expression.append(src, offset, end - offset - 1).append(this.closeToken);
                            offset = end + this.closeToken.length();
                        }

                        if (end == -1) {
                            builder.append(src, start, src.length - start);
                            offset = src.length;
                        } else {
                            builder.append(this.handler.handleToken(expression.toString()));
                            offset = end + this.closeToken.length();
                        }
                    }
                }

                if (offset < src.length) {
                    builder.append(src, offset, src.length - offset);
                }

                return builder.toString();
            }
        } else {
            return "";
        }
    }
}

GenericTokenParser通过parse方法来解析具体的文本,通过openToken(比如 :“${”)和closeToken(比如 :“}”)来找到在文本中的变量的内容,然后将变量的内容,交给它的TokenHandler来处理。

在这里GenericTokenParser就对应上述策略模式类图中的context,而TokenHandler就对应上图中的Strategy接口,TokenHandler的四个不同的实现类,就是四种不同的策略

image-20200822152251732.png

GenericTokenParser根据其所持有的TokenHandler实例,通过调用它的org.apache.ibatis.parsing.TokenHandler#handleToken方法,来执行不同处理变量token的策略。