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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

十一月月底,宿舍樓失火啦,搞得20多天沒有網,目測直到放假也不會來了。。。

?

正題

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

最簡單的入門代碼:

先讀取配置文件流,然后構造個SqlSessionFactory,然后開啟一個SqlSession,指定statement,調用查詢方法,返回結果。那么,你知道他是怎樣實現的嗎

?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 的構造方法來創建一個 XMLConfigBuilder 對象, 調用 XMLConfigBuilder parse 方法進行解析配置文件,返回一個 Configuration 對象

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

?

將返回的 Configuration 對象傳入另外一個重載的 build 方法,實際上是傳入了 DefaultSqlSessionFactory 的構造方法,返回 sqlSessionFactory

我比較關心的是 XMLConfigBuilder parse 方法,都干了什么事情

首先進入 XMLConfigBuilder 的構造方法后, 真正使用配置文件輸入流的是 XPathParser, 它是負責解析 XML文件元素節點的, 通俗地講, XpathParser 負責將原料加工成零件, XMLConfigBuilder 負責按照工序組裝零件成一個產品。

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;}

?

經過構造方法初始化好XPathParser后,就要進入parse方法了。Parse 方法里有個判斷,如果已經解析過了,就會拋出異常,如果沒解析,就將解析標志設為 true。接著調用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 就是工序圖, 組裝產品一定是按照一定順序的, 所以這也是建造者模式的核心。
比如:第一個是全局設置 settings,第二個是屬性文件,第三個是別名。在這里我們看 environmentsElement
很明顯它是來構建 Environment 的, 也就是我們配置的數據源信息。

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")); // 構建事務工廠DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); // 構建數據源工廠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(), 這不就是反射嗎?

而那個 type 則是我們配置文件里面配置的,比如 jdbc 或是 manage, 對應 JdbcTransactionFactory ManagedTransactionFactory

Upooled Pooled對應 UnpooledDataSourceFactory PooledDataSourceFactory

返回 environmentsElement 方法, 我們還看到 Environment 有個 Builder 類, 準確來說是靜態內部類。

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;}}

?

那么這里有一個設計模式的問題。為什么Environment里面要搞一個Builder類呢?直接使用構造方法不也可以達到相同的目的嗎?

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

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

?

SqlSessionFactory的openSession方法

?進入openSession方法

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

?

會發現它調用的是另外一個方法。

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里面的。第二三行的事務工廠和數據源都是在解析配置文件期間裝配到Environment里面的。

到第四行,這是個新東西Executor,簡單來說它是真正執行CRUD操作的工具,給我們提供的SqlSession僅僅是個用戶接口。Executor也是由Configuration對象來創建的,可見Configuration是多么重要。

我們知道事務操作必不可少,所以Executor的創建必須有Transaction對象。

第五行,就是創建SqlSession了,DefaultSqlSession是一個具體實現類。我們可以看到它把Executor傳進去了,那么就不難發現,SqlSession不過是件衣裳。

接下來看SqlSession調用過程

?

調用 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);

我們調用返回一條數據的方法,實際上也是調用selectList,現在看selectList:多個重載方法我就不全部貼了

@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語句的映射,比如:

同樣,這個對象也來自Configuration。接著看,return的是Executor的query方法,ms作為參數。Executor有多個實現類,BaseExecutor是最基礎的實現,來看其中的query實現:

@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語句,腦袋是不是跟配置產生一點聯系了?第二行不需要管,看第三行的實現:

@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;}

?

當查詢的時候先從緩存中找,如果找不到就從數據庫中找,這里我們看

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;}

?

?這句是關鍵:

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;

?

它是需要子類去實現。總共有三種: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運行期間的核心。

StatementHandler是對java.sql.Statement的封裝處理,有三個實現類:CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler。

同樣,利用Configuration來創建一個StatementHandler實例,之后利用這個handler來創建一個java.sql.statement,最后調用handler的<E>query方法,利用原生的Statement來執行查詢操作。

PreparedStatementHandler.query方法

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

?

?前兩行都應該知道,類型轉換以及執行jdbc的查詢。最后一行是利用原生的PreparedStatement來進行結果集的封裝。ResultSetHandler有個默認實現類:DefaultResultSetHandler,具體就不在分析了。

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

?

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

?

轉載于:https://www.cnblogs.com/LUA123/p/8094573.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。