十一月月底,宿舍樓失火啦,搞得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,具體就不在分析了。
到此為止,返回結果集到最上層,顯示給用戶。
?
先寫這些吧,寫的不是特別滿意,望指教~
?