mybatis源碼分析-前奏(一)

hi,大家好,最近生活越來越單一,突然一瞬間覺得要做些什么事情,本來就想著好好工作,卻發(fā)現(xiàn)到處是壁壘,其實歸結(jié)起來還是積累不夠,不善于總結(jié)導致的結(jié)果,未來若干年,我們可能期望遇到問題時,能給一個合理的解決方法。時至今日,我每天都是不斷的在忙碌,忙碌,忙碌...,卻又不知道在忙碌什么,所以希望在此記錄總結(jié)下,以方便日后用到時,能夠快速用到。相當于大腦的一個備份吧。
本位主要講解mybatis代碼分析,mybatis是一個orm框架,目前主流的持久化框架有哪些

框架名 優(yōu)勢 劣勢
hibernate 提供了豐富API,高度封裝 底層sql自動生成,很難優(yōu)化
myabtis 自己編寫sql,可優(yōu)化,可遵循相同的規(guī)范,代碼可自動生成
springjdbc 對原生jdbc封裝,高度靈活 很多代碼需要手寫

封裝性太強,就不容易解耦,不能解耦,就會受限于框架本身,就不足夠靈活。太靈活,又會影響開發(fā)效率,需要手寫太多的代碼。都有各自的優(yōu)劣勢,需要自己根據(jù)場景來選擇
下文是一片關于jdbc、springjdbc、mybatis性能比較的文章
http://blog.sina.com.cn/s/blog_539d361e0100z15p.html

mybatis源碼環(huán)境

gitHub地址:https://github.com/mybatis/mybatis-3.git
建議大家去fork,這樣可以自己方便調(diào)試并提交。也方便記錄。
架構(gòu)圖如下(不知道為啥引用不了圖片連接):

架構(gòu)圖.png

其中基礎支撐層的配置框架模塊應該包含了我們常用的日志模塊、資源加載、反射模塊、類型轉(zhuǎn)換、解析器模塊。

fork下來整個代碼塊結(jié)構(gòu)如下:


代碼結(jié)構(gòu)圖.png

模塊描述.png

我們該如何入手呢,根據(jù)開發(fā)流程,首先接觸的xml配置,當然是先從xml配置解析入手了。進而一步步看它是如何運作的??辞懊婺呛诙炊?,定是那賊巢穴,待俺前去,殺他個干干凈凈。

xml解析

mybatis3.0 xml解析底層是通過DOM、XPath來實現(xiàn)的,其解析時序圖如下:


xml解析時序圖

domparser類圖.png

XPath

XPath是一種查詢xml文檔的語言,就好比SQL語言查詢數(shù)據(jù)庫,通常XPath與DOM進行結(jié)合使用,XPath通過執(zhí)行表達式來獲取xml中的元素。
XPath對應的表達式常用如下:

nodename 選取此節(jié)點的所有子節(jié)點
/ 從根節(jié)點選取
// 從匹配選擇的當前節(jié)點選擇文檔中的節(jié)點,而不考慮它們的位置
. 選取當前節(jié)點
.. 選取當前節(jié)點的父節(jié)點
@ 選取屬性

eg:

//zolfxpathparse_test.xml
<?xml version="1.0" encoding="UTF-8"?>
<cart id="cart1" use="_我啥也不會">
    <order id="昨天的_我啥也不會">
        <product id="1">
            <name>楊梅</name>
            <num>1</num>
            <unit>袋</unit>
            <price>10.00</price>
            <total>10.00</total>
        </product>
        <product id="2">
            <name>脆谷樂</name>
            <num>1</num>
            <unit>盒</unit>
            <price>20.00</price>
            <total>20.00</total>
        </product>
    </order>
</cart>

對上面代碼進行解析:

public class ZolfXPathParseTest {
    @Test
    public void testXmlPath(){
        try {
            //創(chuàng)建document 文檔構(gòu)建器
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            //構(gòu)建document文檔
            Document document = builder.parse("D:\\mybatis3\\src\\test\\java\\resources\\zolfxpathparse_test.xml");
            //創(chuàng)建解析構(gòu)建器
            XPathFactory xPathFactory = XPathFactory.newInstance();
            //構(gòu)建解析器
            XPath xPath = xPathFactory.newXPath();
            //解析表達式
            XPathExpression expr = xPath.compile("http://product[name='楊梅']/price/text()");
            //執(zhí)行解析
            Object obj = expr.evaluate(document, XPathConstants.NODESET);
            NodeList nodes = (NodeList)obj;
            for(int i=0;i<nodes.getLength();i++){
                System.out.println("====="+nodes.item(i).getNodeValue());
                //=====10.00
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }
    }
}

通過上面代碼我們可以知道XPath其實就是從xml中獲取所需的元素的。對xml的構(gòu)建dom樹的過程是DocumentBuilder實現(xiàn)的。

XPathParser.java

mybatis中主要通過XPathParser來進行解析。其代碼如下:

  private final Document document;//domcument對象
  private boolean validation;//是否開啟驗證,
  private EntityResolver entityResolver;//用于加載本地DTD文件
  private Properties variables;//mybatis-config.xml中屬性集合
  private XPath xpath;//上文序列圖中XPath對象

對應的解析時:
提供了evalString、evalLong、evalBooleal、evalInteger等解析方法。

//XPathParser.java
  public String evalString(Object root, String expression) {
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    result = PropertyParser.parse(result, variables);
    return result;
  }

他們都調(diào)用了evaluate方法

//XPathParser.java
private Object evaluate(String expression, Object root, QName returnType) {
    try {
      return xpath.evaluate(expression, root, returnType);
    } catch (Exception e) {
      throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
    }
  }

PropertyParser.java

解析時序圖.png

eg:下面代碼解析

 Properties props = new Properties();
    props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
    props.setProperty("key", "value");
    props.setProperty("tableName", "members");
    props.setProperty("orderColumn", "member_id");
    props.setProperty("a:b", "c");//冒號是默認分隔符,冒號后面代表默認值
PropertyParser.parse("SELECT * FROM ${tableName:users} ORDER BY ${orderColumn:id}", props)
//ProperyParser.java
 public static String parse(String string, Properties variables) {
//占位符處理器
    VariableTokenHandler handler = new VariableTokenHandler(variables);
//占位符解析器
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }
//GenericTokenParser.java
public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // 搜索占位符起始位置
    int start = text.indexOf(openToken, 0);
    if (start == -1) {
      return text;
    }
    //占位符轉(zhuǎn)字節(jié)
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
//占位符的值 eg:value:aaa
    StringBuilder expression = null;
    while (start > -1) {
      if (start > 0 && src[start - 1] == '\\') {
        // 如果是開始轉(zhuǎn)義字符,則直接將前面字符串添加到builder.
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {
        // 搜索占位符結(jié)束位置
        if (expression == null) {
          expression = new StringBuilder();
        } else {
          expression.setLength(0);
        }
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        int end = text.indexOf(closeToken, offset);
        while (end > -1) {
          if (end > offset && src[end - 1] == '\\') {
            //如果是轉(zhuǎn)義結(jié)束字符,則繼續(xù)
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
         builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
         //將expression值交給TokenHandler處理,并將處理結(jié)果添加到builer中 builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }

代碼調(diào)試信息如下:

parser調(diào)試信息.png

TokenHandler主要從properties對象中獲取expression的值,如果沒有則返回默認值
tokenHandler類圖.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容