mybatis源码分析(方法调用过程)
十一月月底,宿舍樓失火啦,搞得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ù)源信息。
主要有兩大塊: 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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 据说有99%的人都会做错的面试题
- 下一篇: 如何避免踩坑--初创技术团队组建风险预估