日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

mybatis源码分析(方法调用过程)

發(fā)布時(shí)間:2025/3/8 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis源码分析(方法调用过程) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

十一月月底,宿舍樓失火啦,搞得20多天沒有網(wǎng),目測(cè)直到放假也不會(huì)來了。。。

?

正題

嗯~,其實(shí)閱讀源碼不是為了應(yīng)付面試,更重要的讓你知道,大師是怎樣去寫代碼的,同樣是用Java,為啥Clinton Begin寫的叫源碼,而你寫只能叫代碼。

最簡(jiǎn)單的入門代碼:

先讀取配置文件流,然后構(gòu)造個(gè)SqlSessionFactory,然后開啟一個(gè)SqlSession,指定statement,調(diào)用查詢方法,返回結(jié)果。那么,你知道他是怎樣實(shí)現(xiàn)的嗎

?SqlSessionFactoryBuilder.build 方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error. }}}

將輸入流傳入 XMLConfigBuilder 的構(gòu)造方法來創(chuàng)建一個(gè) XMLConfigBuilder 對(duì)象, 調(diào)用 XMLConfigBuilder parse 方法進(jìn)行解析配置文件,返回一個(gè) Configuration 對(duì)象

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

?

將返回的 Configuration 對(duì)象傳入另外一個(gè)重載的 build 方法,實(shí)際上是傳入了 DefaultSqlSessionFactory 的構(gòu)造方法,返回 sqlSessionFactory

我比較關(guān)心的是 XMLConfigBuilder parse 方法,都干了什么事情

首先進(jìn)入 XMLConfigBuilder 的構(gòu)造方法后, 真正使用配置文件輸入流的是 XPathParser, 它是負(fù)責(zé)解析 XML文件元素節(jié)點(diǎn)的, 通俗地講, XpathParser 負(fù)責(zé)將原料加工成零件, XMLConfigBuilder 負(fù)責(zé)按照工序組裝零件成一個(gè)產(chǎn)品。

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}

?

經(jīng)過構(gòu)造方法初始化好XPathParser后,就要進(jìn)入parse方法了。Parse 方法里有個(gè)判斷,如果已經(jīng)解析過了,就會(huì)拋出異常,如果沒解析,就將解析標(biāo)志設(shè)為 true。接著調(diào)用parseConfiguration?

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {Properties settings = settingsAsPropertiess(root.evalNode("settings"));//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));loadCustomVfs(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectionFactoryElement(root.evalNode("reflectionFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

?

parseConfiguration 就是工序圖, 組裝產(chǎn)品一定是按照一定順序的, 所以這也是建造者模式的核心。
比如:第一個(gè)是全局設(shè)置 settings,第二個(gè)是屬性文件,第三個(gè)是別名。在這里我們看 environmentsElement
很明顯它是來構(gòu)建 Environment 的, 也就是我們配置的數(shù)據(jù)源信息。

private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {environment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); // 構(gòu)建事務(wù)工廠DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); // 構(gòu)建數(shù)據(jù)源工廠DataSource dataSource = dsFactory.getDataSource();Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}

主要有兩大塊: transactionManagerElement dataSourceElement

private TransactionFactory transactionManagerElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");Properties props = context.getChildrenAsProperties();TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();factory.setProperties(props);return factory;}throw new BuilderException("Environment declaration requires a TransactionFactory.");}private DataSourceFactory dataSourceElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");Properties props = context.getChildrenAsProperties();DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();factory.setProperties(props);return factory;}throw new BuilderException("Environment declaration requires a DataSourceFactory.");}

注意看: resolveClass(type).newInstance(), 這不就是反射嗎?

而那個(gè) type 則是我們配置文件里面配置的,比如 jdbc 或是 manage, 對(duì)應(yīng) JdbcTransactionFactory ManagedTransactionFactory

Upooled Pooled對(duì)應(yīng) UnpooledDataSourceFactory PooledDataSourceFactory

返回 environmentsElement 方法, 我們還看到 Environment 有個(gè) Builder 類, 準(zhǔn)確來說是靜態(tài)內(nèi)部類。

Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());

?

具體的Environment類:

public final class Environment {private final String id;private final TransactionFactory transactionFactory;private final DataSource dataSource;public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {if (id == null) {throw new IllegalArgumentException("Parameter 'id' must not be null");}if (transactionFactory == null) {throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");}this.id = id;if (dataSource == null) {throw new IllegalArgumentException("Parameter 'dataSource' must not be null");}this.transactionFactory = transactionFactory;this.dataSource = dataSource;}public static class Builder {private String id;private TransactionFactory transactionFactory;private DataSource dataSource;public Builder(String id) {this.id = id;}public Builder transactionFactory(TransactionFactory transactionFactory) {this.transactionFactory = transactionFactory;return this;}public Builder dataSource(DataSource dataSource) {this.dataSource = dataSource;return this;}public String id() {return this.id;}public Environment build() {return new Environment(this.id, this.transactionFactory, this.dataSource);}}public String getId() {return this.id;}public TransactionFactory getTransactionFactory() {return this.transactionFactory;}public DataSource getDataSource() {return this.dataSource;}}

?

那么這里有一個(gè)設(shè)計(jì)模式的問題。為什么Environment里面要搞一個(gè)Builder類呢?直接使用構(gòu)造方法不也可以達(dá)到相同的目的嗎?

1. 首先, 用內(nèi)部類是因?yàn)閮?nèi)部類與外部類有一定的關(guān)系, 往往只有該外部類調(diào)用此內(nèi)部類。 靜態(tài)內(nèi)部類
只能訪問靜態(tài)的成員變量和方法,不能訪問非靜態(tài)變量的方法。但是普通內(nèi)部類可以訪問任意外部類
的成員變量和方法。靜態(tài)內(nèi)部類可以聲明普通成員變量和方法,但是普通內(nèi)部類不能聲明 static 變量
或方法。
靜態(tài)內(nèi)部類: Inner I = new Outer.Inner();
普通內(nèi)部類: Outer o = new Outer(); Inner I = o.new Inner();
2. 另外, 靜態(tài)都是用來修飾類的內(nèi)部成員的, 比如靜態(tài)方法, 靜態(tài)成員變量。 靜態(tài)方法不能訪問非靜態(tài)
變量和非靜態(tài)方法。 Static 不能修飾局部變量。
3. 總結(jié):如果類的構(gòu)造函數(shù)有多個(gè)參數(shù),設(shè)計(jì)這樣的類時(shí), 最好使用 Builder 模式, 特別是大多數(shù)參數(shù)
都是可選的時(shí)候。如果現(xiàn)在不能確定參數(shù)的個(gè)數(shù),最好一開始就使用建造者模式。?

?到此,SqlSessionFactoryBuilder.build方法的作用是:解析配置文件,構(gòu)建唯一的Configuration對(duì)象,構(gòu)建全局唯一并且線程安全的SqlSessionFactory。

?

SqlSessionFactory的openSession方法

?進(jìn)入openSession方法

@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}

?

會(huì)發(fā)現(xiàn)它調(diào)用的是另外一個(gè)方法。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

?

看try塊第一行,獲得的Environment是前面由XMLConfigBuilder裝配到Configuration里面的。第二三行的事務(wù)工廠和數(shù)據(jù)源都是在解析配置文件期間裝配到Environment里面的。

到第四行,這是個(gè)新東西Executor,簡(jiǎn)單來說它是真正執(zhí)行CRUD操作的工具,給我們提供的SqlSession僅僅是個(gè)用戶接口。Executor也是由Configuration對(duì)象來創(chuàng)建的,可見Configuration是多么重要。

我們知道事務(wù)操作必不可少,所以Executor的創(chuàng)建必須有Transaction對(duì)象。

第五行,就是創(chuàng)建SqlSession了,DefaultSqlSession是一個(gè)具體實(shí)現(xiàn)類。我們可以看到它把Executor傳進(jìn)去了,那么就不難發(fā)現(xiàn),SqlSession不過是件衣裳。

接下來看SqlSession調(diào)用過程

?

調(diào)用 SqlSession selectList 方法

先看selectOne方法

@Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.<T>selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}

?

看到這句話沒

this.<T>selectList(statement, parameter);

我們調(diào)用返回一條數(shù)據(jù)的方法,實(shí)際上也是調(diào)用selectList,現(xiàn)在看selectList:多個(gè)重載方法我就不全部貼了

@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

?

MappedStatement你可以理解為你配置文件里面寫的sql語(yǔ)句的映射,比如:

同樣,這個(gè)對(duì)象也來自Configuration。接著看,return的是Executor的query方法,ms作為參數(shù)。Executor有多個(gè)實(shí)現(xiàn)類,BaseExecutor是最基礎(chǔ)的實(shí)現(xiàn),來看其中的query實(shí)現(xiàn):

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

?

第一行,從ms里面獲得綁定的sql語(yǔ)句,腦袋是不是跟配置產(chǎn)生一點(diǎn)聯(lián)系了?第二行不需要管,看第三行的實(shí)現(xiàn):

@SuppressWarnings("unchecked")@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601 deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482 clearLocalCache();}}return list;}

?

當(dāng)查詢的時(shí)候先從緩存中找,如果找不到就從數(shù)據(jù)庫(kù)中找,這里我們看

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

?

?這句是關(guān)鍵:

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

在BaseExecutor里面有定義

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;

?

它是需要子類去實(shí)現(xiàn)。總共有三種:BatchExecutor,ReuseExecutor,SimpleExecutor。這里我們選擇SimpleExecutor:

@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}

?

?這里說一句題外話:到處都需要Configuration,它無疑是mybatis運(yùn)行期間的核心。

StatementHandler是對(duì)java.sql.Statement的封裝處理,有三個(gè)實(shí)現(xiàn)類:CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler。

同樣,利用Configuration來創(chuàng)建一個(gè)StatementHandler實(shí)例,之后利用這個(gè)handler來創(chuàng)建一個(gè)java.sql.statement,最后調(diào)用handler的<E>query方法,利用原生的Statement來執(zhí)行查詢操作。

PreparedStatementHandler.query方法

@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleResultSets(ps);}

?

?前兩行都應(yīng)該知道,類型轉(zhuǎn)換以及執(zhí)行jdbc的查詢。最后一行是利用原生的PreparedStatement來進(jìn)行結(jié)果集的封裝。ResultSetHandler有個(gè)默認(rèn)實(shí)現(xiàn)類:DefaultResultSetHandler,具體就不在分析了。

到此為止,返回結(jié)果集到最上層,顯示給用戶。

?

先寫這些吧,寫的不是特別滿意,望指教~

?

轉(zhuǎn)載于:https://www.cnblogs.com/LUA123/p/8094573.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的mybatis源码分析(方法调用过程)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。