[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
对象就根据配置文件建立出来了。
此文章仅表明本身(本菜鸟)学习积累记录,或者学习笔记,若有侵权,请联系做者删除。人无完人,文章也同样,文笔稚嫩,在下不才,勿喷,若是有错误之处,还望指出,感激涕零~
技术之路不在一时,山高水长,纵使缓慢,驰而不息。
公众号:秦怀杂货店