Mybatis 源碼分析(一)之 Mybatis 的Executor的初始化

2.jpg

Mybatis 源碼分析(一)之 Mybatis 的Executor的初始化

Mybatis系列:
Mybatis 基礎(chǔ)介紹與逆向工程的構(gòu)建 :http://www.reibang.com/p/1c18db4d7a38
Mybatis 源碼分析(一)之 Mybatis 的Executor的初始化:http://www.reibang.com/p/c7425c841337
Mybatis 源碼分析(二)之 Mybatis 操作數(shù)據(jù)庫的流程 :http://www.reibang.com/p/11d354ec3612
Mybatis 源碼分析(三)之 Mybatis 的一級(jí)緩存和二級(jí)緩存 :http://www.reibang.com/p/5515640d14fe


先看一下mybatis中幾個(gè)核心的概念名詞

名稱 意義
Configuration 管理 mybatis-config.xml 全局配置關(guān)
SqlSessionFactor Session 管理工廠
Session SqlSession 是一個(gè)面向用戶(程序員)的接口叼丑。SqlSession中提供了很多操作數(shù)據(jù)庫的方法
Executor 執(zhí)行器是一個(gè)接口(基本執(zhí)行器、緩存執(zhí)行器)作用:SqlSession內(nèi)部通過執(zhí)行器操作數(shù)據(jù)庫
MappedStatement 底層封裝對(duì)象作用:對(duì)操作數(shù)據(jù)庫存儲(chǔ)封裝,包括sql語句屎勘、輸入輸出參數(shù)
StatementHandle 具體操作數(shù)據(jù)庫相關(guān)的 handler 接口
ResultSetHandler 具體操作數(shù)據(jù)庫返回結(jié)果的 handler

看下mybatis的源碼結(jié)構(gòu):

├─annotations ->注解相關(guān) 比如 select insert
├─binding -> mapper 相關(guān)
├─builder ->解析 xml 相關(guān)
├─cache ->緩存
├─cursor -> 返回結(jié)果 resultset
├─datasourcer ->數(shù)據(jù)管理
├─exceptionsr -> 異常
├─executorr -> 執(zhí)行器
├─io ->classloader
├─jdbc ->jdbc
├─lang ->jdk7 jdk8
├─logging ->日志相關(guān)
├─mapping ->mapper 相關(guān)的封裝
├─parsing ->xml 相關(guān)解析
├─plugin ->攔截器
├─reflection ->反射相關(guān)
├─scripting ->數(shù)據(jù)廠家
├─session ->sessiomn
├─transaction ->事務(wù)
└─type ->返回類型

具體查看方法钟鸵,可以先下載一下maven的源碼:https://github.com/mybatis/mybatis-3/releases

然后進(jìn)入導(dǎo)入我們的IDE中性昭,然后進(jìn)入到j(luò)ava目錄渣蜗,進(jìn)入到終端(Terminal)里面輸入tree,IDE已經(jīng)集成Maven屠尊,所以就不需要mvn命令了。具體如下耕拷,上面是整理版的讼昆。

└─java
    └─org
        └─apache
            └─ibatis
                ├─annotations
                ├─binding
                ├─builder
                │  ├─annotation
                │  └─xml
                ├─cache
                │  ├─decorators
                │  └─impl
                ├─cursor
                │  └─defaults
                ├─datasource
                │  ├─jndi
                │  ├─pooled
                │  └─unpooled
                ├─exceptions
                ├─executor
                │  ├─keygen
                │  ├─loader
                │  │  ├─cglib
                │  │  └─javassist
                │  ├─parameter
                │  ├─result
                │  ├─resultset
                │  └─statement
                ├─io
                ├─jdbc
                ├─lang
                ├─logging
                │  ├─commons
                │  ├─jdbc
                │  ├─jdk14
                │  ├─log4j
                │  ├─log4j2
                │  ├─nologging
                │  ├─slf4j
                │  └─stdout
                ├─mapping
                ├─parsing
                ├─plugin
                ├─reflection
                │  ├─factory
                │  ├─invoker
                │  ├─property
                │  └─wrapper
                ├─scripting
                │  ├─defaults
                │  └─xmltags
                ├─session
                │  └─defaults
                ├─transaction
                │  ├─jdbc
                │  └─managed
                └─type

拿我們之前寫的demo,查看源碼里面是怎么寫的骚烧,如下面這個(gè)浸赫。

@Test
public void test01() throws IOException {
    String resource = "mybatis-config.xml";
    Reader reader = Resources.getResourceAsReader(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sqlSessionFactory.openSession();
    User user = session.selectOne("selectByPrimaryKey", 1);
    session.commit();
    logger.info(user.toString());
}

這個(gè)很好理解,以流的方式讀取我們的config文件赃绊。

Reader reader = Resources.getResourceAsReader(resource);

然后來看SqlSessionFactory是怎么build創(chuàng)建的既峡。(下面只展示核心代碼)

new SqlSessionFactoryBuilder().build(reader);

進(jìn)入看一下。

public SqlSessionFactory build(Reader reader) {
    return this.build((Reader)reader, (String)null, (Properties)null);
}

繼續(xù)進(jìn)入

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
   XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
   SqlSessionFactory  var5 = this.build(parser.parse());
   return var5;
}

這時(shí)候可以看到實(shí)例化了一個(gè)核心的類XMLConfigBuilder碧查,然后parser.parse()运敢,進(jìn)行解析,我們點(diǎn)進(jìn)去么夫。

public Configuration parse() {
    if (this.parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
        this.parsed = true;
        this.parseConfiguration(this.parser.evalNode("/configuration"));
        return this.configuration;
    }
}

重點(diǎn)看這個(gè)

this.parseConfiguration(this.parser.evalNode("/configuration"));

繼續(xù)點(diǎn)進(jìn)去

private void parseConfiguration(XNode root) {
    try {
        this.propertiesElement(root.evalNode("properties"));
        this.typeAliasesElement(root.evalNode("typeAliases"));
        this.pluginElement(root.evalNode("plugins"));
        this.objectFactoryElement(root.evalNode("objectFactory"));
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
        this.settingsElement(root.evalNode("settings"));
        this.environmentsElement(root.evalNode("environments"));
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
}

OK者冤,就是我們之前說的mybatis-config.xml里面的屬性肤视。

mybatis-config.xml
屬性名 作用
屬性(peoperties) 系統(tǒng)屬性占用配置
設(shè)置(settings) 用于修改Mybatis的運(yùn)行時(shí)行為
類型別名(typeAliases) 為類型建立別名档痪,一般使用更短的名稱代替
類型處理器(typeHanders) 用于將預(yù)編譯語句(PreparedStatement)或者結(jié)果集(ResultSet)中的JDBC類型轉(zhuǎn)換為Java類型
對(duì)象工廠(ObjectFactory) 提供默認(rèn)構(gòu)造器或執(zhí)行構(gòu)造器參數(shù)初始化目標(biāo)類型的對(duì)象
插件(plugins) Mybatis提供插件的方式來攔截映射(可以根據(jù)自己的需求進(jìn)行編寫插件)
環(huán)境(environments) Mybatis運(yùn)行配置多個(gè)環(huán)境
數(shù)據(jù)庫標(biāo)識(shí)提供商(databaseIdProvider) 數(shù)據(jù)庫標(biāo)識(shí)提供商
SQL映射文件(mappers) SQL映射文件

中間有將我們的配置封裝Node對(duì)象

public XNode evalNode(String expression) {
    return this.evalNode(this.document, expression);
}

public XNode evalNode(Object root, String expression) {
    Node node = (Node)this.evaluate(expression, root, XPathConstants.NODE);
    return node == null ? null : new XNode(this, node, this.variables);
}

在之后,返回一個(gè)DefaultSqlSessionFactory

public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
}

整體的一個(gè)調(diào)用鏈?zhǔn)沁@樣的

org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
 >org.apache.ibatis.builder.xml.XMLConfigBuilder 實(shí)例構(gòu)造函數(shù)
   >org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
     >org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration 解析mybatis-config.xml中的內(nèi)容
       >org.apache.ibatis.parsing.XPathParser.evaluate
        >org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
         >org.apache.ibatis.session.SqlSessionFactoryBuilder.build(org.apache.ibatis.session.Configuration)
          >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.DefaultSqlSessionFactory 返回一個(gè)DefaultSqlSessionFactory

整個(gè)流程就是返回一個(gè)我們下一步需要的DefaultSqlSessionFactory邢滑。

下一段代碼

SqlSession session = sqlSessionFactory.openSession();

同樣的腐螟,一步步點(diǎn)進(jìn)去

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}

繼續(xù)

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

其中 transactionFactory.newTransaction 返回一個(gè)TransactionFactory

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

然后

Executor executor = this.configuration.newExecutor(tx, execType);

返回一個(gè)執(zhí)行器 Executor

Executor:執(zhí)行器是一個(gè)接口(基本執(zhí)行器、緩存執(zhí)行器)

作用:SqlSession內(nèi)部通過執(zhí)行器操作數(shù)據(jù)庫

然后繼續(xù)進(jìn)去

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? this.defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Object executor;
    //批處理執(zhí)行器
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    //可重復(fù)用的執(zhí)行器
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    
    //cacheEnabled默認(rèn)是true困后,傳入的參數(shù)是SimpleExecutor乐纸,裝飾成CachingExecutor
    if (this.cacheEnabled) {
        executor = new CachingExecutor((Executor)executor);
    }

    //這個(gè)是攔截器鏈,責(zé)任鏈模式攔截器摇予,不過需要我們自己實(shí)現(xiàn)
    Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
    return executor;
}

最后返回給我們一個(gè)executor汽绢,因?yàn)閙ybatis默認(rèn)開啟緩存,所以返回是CachingExecutor侧戴,但是如果沒有在mybatis-config.xml中開啟二級(jí)緩存宁昭,在后面的query查詢中MappedStatement.getCache()時(shí)跌宛,返回的還是空的,在之后用delegate.query(...)進(jìn)行查詢积仗,那時(shí)executor是SimpleExecutor疆拘。

Executor類圖

Executor.png

如有問題,歡迎留言:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寂曹,一起剝皮案震驚了整個(gè)濱河市哎迄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌隆圆,老刑警劉巖漱挚,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渺氧,居然都是意外死亡棱烂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門阶女,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颊糜,“玉大人,你說我怎么就攤上這事秃踩〕挠悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵憔杨,是天一觀的道長鸟赫。 經(jīng)常有香客問我,道長消别,這世上最難降的妖魔是什么抛蚤? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮寻狂,結(jié)果婚禮上岁经,老公的妹妹穿的比我還像新娘。我一直安慰自己蛇券,他們只是感情好缀壤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纠亚,像睡著了一般塘慕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒂胞,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天图呢,我揣著相機(jī)與錄音,去河邊找鬼。 笑死蛤织,一個(gè)胖子當(dāng)著我的面吹牛拥娄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞳筏,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼稚瘾,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了姚炕?” 一聲冷哼從身側(cè)響起摊欠,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柱宦,沒想到半個(gè)月后些椒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掸刊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年免糕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忧侧。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡石窑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出松逊,到底是詐尸還是另有隱情,我是刑警寧澤肯夏,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布经宏,位于F島的核電站,受9級(jí)特大地震影響驯击,放射性物質(zhì)發(fā)生泄漏烁兰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一徊都、第九天 我趴在偏房一處隱蔽的房頂上張望沪斟。 院中可真熱鬧,春花似錦碟贾、人聲如沸币喧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至干发,卻和暖如春朱巨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枉长。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工冀续, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留琼讽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓洪唐,卻偏偏與公主長得像钻蹬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凭需,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容