[TOC]
java
咱們使用sqlSession
以前,須要去獲取配置文件,獲取InputStream
輸入流,經過SqlSessionFactoryBuilder
獲取sqlSessionFactory
對象,從而獲取sqlSession
。sql
InputStream is = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession();
1.首先咱們來看InputStream is = Resources.getResourceAsStream("mybatis.xml");
這句話到底替咱們幹了什麼,下面能夠看出在裏面調用了另外一個內部方法,resource是全局配置的文件名:數組
public static InputStream getResourceAsStream(String resource) throws IOException { // 從這裏字面意思是傳一個空的類加載器進去,還有全局配置文件名,從方法名的意思就是 // 將配置文件讀取,轉化成輸入流 return getResourceAsStream((ClassLoader)null, resource); }
2.跟進方法中,咱們能夠知道在裏面調用ClassLoaderWrapper
類的一個實例對象的getResourceAsStream()
方法,這個classLoaderWrapper
怎麼來的呢?這個是Resources.class
的一個成員屬性,那麼這個ClassLoaderWrapper
是什麼東西呢?緩存
在Resources.class
中咱們只是使用private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
建立一個classLoaderWrapper
對象。ClassLoaderWrapper
實際上是一個ClassLoader
(類加載器)的包裝類,其中包含了幾個ClassLoader
對象,一個defaultClassLoader
,一個systemClassLoader
,經過內部控制,能夠確保返回正確的類加載器給系統使用。咱們能夠當成一個mybatis
自定義過的類加載器。session
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } else { return in; } }
3.咱們能夠看出調用了下面這個內部方法,裏面調用了封裝的方法,一個是獲取當前的類加載器,另外一個是傳進來的文件名:mybatis
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return this.getResourceAsStream(resource, this.getClassLoaders(classLoader)); }
4.查看getClassLoaders()
這個方法,能夠看到裏面初始化了一個類加載器的數組,裏面有不少個類加載器,包括默認的類加載器,當前線程的上下文類加載器,系統類加載器等。app
ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{classLoader, this.defaultClassLoader, Thread.currentThread().getContextClassLoader(), this.getClass().getClassLoader(), this.systemClassLoader}; }
5.進入getResourceAsStream(String resource, ClassLoader[] classLoader)
這個方法內部,咱們能夠看到裏面遍歷全部的類加載器,使用類加載器來加載獲取InputStream
對象,咱們能夠知道里面是選擇第一個適合的類加載器,若是咱們不傳類加載器進去,那麼第一個本身定義的類加載器就是null,那麼就會默認選擇第二個默認類加載器,並且咱們能夠知道若是文件名前面沒有加「/
」,獲取到空對象的話,會自動加上「/
」再訪問一遍:ide
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { ClassLoader[] arr$ = classLoader; int len$ = classLoader.length; for(int i$ = 0; i$ < len$; ++i$) { ClassLoader cl = arr$[i$]; if (null != cl) { InputStream returnValue = cl.getResourceAsStream(resource); if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null; }
6.咱們進入類加載器加載資源文件的代碼中,咱們能夠看到首先獲取全路徑的url
,而後再調用openStream()
:學習
public InputStream getResourceAsStream(String name) { URL url = getResource(name); try { return url != null ? url.openStream() : null; } catch (IOException e) { return null; } }
6.1.咱們跟進getResource(name)
這個方法,咱們能夠看到裏面都是調用parent
的getResource()
方法,若是已是父加載器,那麼就使用getBootstrapResource(name)
獲取,若是獲取出來是空的,再根據getBootstrapResource(name)
方法獲取。ui
public URL getResource(String name) { URL url; if (parent != null) { url = parent.getResource(name); } else { url = getBootstrapResource(name); } if (url == null) { url = findResource(name); } return url; }
6.1.1咱們跟進去getBootstrapResource(name);
private static URL getBootstrapResource(String name) { URLClassPath ucp = getBootstrapClassPath(); Resource res = ucp.getResource(name); return res != null ? res.getURL() : null; }
6.1.1.1咱們看到getBootstrapClassPath()
這個方法,這個方法的裏面調用了引入的包,讀取的是類加載器的加載路徑,這個方法到此爲止,再深刻就回不去了:)。
static URLClassPath getBootstrapClassPath() { return sun.misc.Launcher.getBootstrapClassPath(); }
6.1.1.2 咱們看ucp.getResource(name)
這個方法,咱們能夠看到在裏面調用了這個方法,這個方法主要是查找緩存,而後遍歷找到第一個符合條件的加載器來獲取resource
,到此咱們再也不深究下去,得往上一層回頭看:
public Resource getResource(String var1, boolean var2) { if (DEBUG) { System.err.println("URLClassPath.getResource(\"" + var1 + "\")"); } int[] var4 = this.getLookupCache(var1); URLClassPath.Loader var3; for(int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) { Resource var6 = var3.getResource(var1, var2); if (var6 != null) { return var6; } } return null; }
咱們知道getBootstrapResource(name)
裏面主要是url(文件資源的路徑),而後使用url.openStream()
去獲取stream流:
public final InputStream openStream() throws java.io.IOException { return openConnection().getInputStream(); }
咱們來看openConnection()方法,裏面調用的是一個抽象方法,獲取的是一個URLConnection(url鏈接對象):
public URLConnection openConnection() throws java.io.IOException { return handler.openConnection(this); }
再看getInputStream()這個方法,咱們能夠看到這是一個接口方法,咱們找到FileURLConnection的這個方法,這是一個單線程處理文件URL的inputstream的方法:
public synchronized InputStream getInputStream() throws IOException { this.connect(); if (this.is == null) { if (!this.isDirectory) { throw new FileNotFoundException(this.filename); } FileNameMap var3 = java.net.URLConnection.getFileNameMap(); StringBuffer var4 = new StringBuffer(); if (this.files == null) { throw new FileNotFoundException(this.filename); } Collections.sort(this.files, Collator.getInstance()); for(int var5 = 0; var5 < this.files.size(); ++var5) { String var6 = (String)this.files.get(var5); var4.append(var6); var4.append("\n"); } this.is = new ByteArrayInputStream(var4.toString().getBytes()); } return this.is; }
到這裏,整個獲取inputstream的過程已經結束,只要把返回值往上一層返回就能夠獲得這個配置文件所須要的inputstream。
首先SqlSessionFactoryBuilder的無參數構造方法是沒有任何操做的:
public SqlSessionFactoryBuilder() { }
那麼咱們看build(is)
這個方法,裏面調用了一個封裝方法,一個是inputstream,一個是string,一個是屬性對象:
public SqlSessionFactory build(InputStream inputStream) { return this.build((InputStream)inputStream, (String)null, (Properties)null); }
跟進去,咱們能夠看到在裏面使用了xmlconfigbuilder,也就是xml配置構造器,實例化一個xml配置對象,可想而知,也就是咱們的mybatis.xml所對應的配置對象構造器,在裏面調用了另外一個build()方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException var13) { ; } } return var5; }
咱們能夠看到調用的另外一個build方法,也就是使用配置對象構建一個DefaultSqlSessionFactory對象,在上面返回這個對象,也就是咱們的sqlsessionFactory。
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
咱們能夠看到其實這個是sqlSessionFactory
的一個接口,其實現類是DefaultSqlSessionFactory
,那麼方法以下:
public SqlSession openSession() { return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); }
咱們查看openSessionFromDataSource()
這個方法,從名字能夠大概知道是從數據源加載Sqlsession,裏面能夠指定執行器類型,事物隔離級別,還有是否自動提交,若是不設定,那麼默認是null以及false,在方法內主要作的是將配置文件對象的環境取出來構造事務工廠,配置執行器等,返回一個DefaultSqlSession
的實例。
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; }
到此爲止,一個sqlsession
對象就根據配置文件建立出來了。
此文章僅表明本身(本菜鳥)學習積累記錄,或者學習筆記,若有侵權,請聯繫做者刪除。人無完人,文章也同樣,文筆稚嫩,在下不才,勿噴,若是有錯誤之處,還望指出,感激涕零~
技術之路不在一時,山高水長,縱使緩慢,馳而不息。
公衆號:秦懷雜貨店