Mybatis【2.1】-- 从读取流到创建SqlSession发生了什么?
我们使用sqlSession
之前,需要去获取配置文件,获取InputStream
输入流,通过SqlSessionFactoryBuilder
获取sqlSessionFactory
对象,从而获取sqlSession
。
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
1.Resources.getResourceAsStream("mybatis.xml")到底做了什么?
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
自定义过的类加载器。
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.我们可以看出调用了下面这个内部方法,里面调用了封装的方法,一个是获取当前的类加载器,另一个是传进来的文件名:
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return this.getResourceAsStream(resource, this.getClassLoaders(classLoader));
}
4.查看getClassLoaders()
这个方法,可以看到里面初始化了一个类加载器的数组,里面有很多个类加载器,包括默认的类加载器,当前线程的上下文类加载器,系统类加载器等。
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,那么就会默认选择第二个默认类加载器,而且我们可以知道如果文件名前面没有加“/
”,获取到空对象的话,会自动加上“/
”再访问一遍:
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)
方法获取。
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。
2. new SqlSessionFactoryBuilder().build(is)的运行原理
首先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);
}
3. sqlSessionFactory.openSession()获取sqlSession
我们可以看到其实这个是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
对象就根据配置文件创建出来了。
此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~
技术之路不在一时,山高水长,纵使缓慢,驰而不息。
公众号:秦怀杂货店
Mybatis【2.1】-- 从读取流到创建SqlSession发生了什么?的更多相关文章
- javaSE中的输入输出流---一个读取流,相应多个输出流。并且生成的碎片文件都有有序的编号
<span style="font-size:18px;">package com.io.other.split; import java.io.File; impor ...
- Mybatis【2.2】-- Mybatis关于创建SqlSession源码分析的几点疑问?
代码直接放在Github仓库[https://github.com/Damaer/Mybatis-Learning ],可直接运行,就不占篇幅了. 目录 1.为什么我们使用SQLSessionFact ...
- 【NPOI】通过NPOI从内存流中创建EXCEL
一言不合就开始帖代码 XSSFWorkbook workbook = new XSSFWorkbook(); //创建工作簿 XSSFSheet sheet = (XSSFSheet)workbook ...
- openvswitch datapath 内核态流表创建过程(ovs_flow_cmd_new)
datapath流表更新的入口函数都定义在dp_flow_genl_ops中,流表创建的入口函数是ovs_flow_cmd_new函数,通过该函数,我们可以一窥流表相关信息的建立. 1.ovs_flo ...
- SDNLAB技术分享(四):利用ODL下发流表创建VxLAN网络
邓晓涛,当前就职于江苏省未来网络创新研究院,是CDN团队的一名研发人员,主要从事SDN相关的研发相关工作.曾就职于三星电子于先行解决方案研发组任高级工程师.思科系统于云协作应用技术部(CCATG)任工 ...
- ASP.NET Core Action 读取流
以前mvc5 action可以直接使用 var stream = HttpContext.Current.Request.InputStream; 读取流,在Core中有所不同,可以使用以下方式读取 ...
- 接收端通过Request.InputStream读取流
以下有两种方式可以获取响应的数据流 1. 接收端通过Request.InputStream读取流 public static string StreamRead() { byte[] byts = n ...
- mybatis源码分析之03SqlSession的创建
在上一篇中,说到了mybatis是如何构造一个SqlSessionFactory实例的,顾名思意,SqlSessionFactory就是用于创建SqlSession的工厂类. 好,现在我们接着昨天的来 ...
- python读取txt批量创建文件
python读取txt批量创建文件 pythonbatchfile 前几天有个小问题, 需要批量建立很多文件夹,, 所以手动写了个小的脚本, 后续可以直接使用 读取目录文件, 然后直接创建相应的文件 ...
随机推荐
- Python ( 高级 第二部)
目录 模块和包 面向对象 部分一: 面向对象程序开发 面向对象封装: 对象的相关操作 面向对象封装: 类的相关操作 实例化的对象/ 定义的类删除公有成员属性和公有成员方法 部分二: 单继承 多继承 菱 ...
- NB-IoT应用分类与技术特点分析
NB-Iot作为一种窄带物联网技术在各大行业脱颖而出,其应用涵盖多个领域.此文计讯小编将讲解NB-IoT的主要应用分类及相关特点. 一.NB-IoT是什么 NB-IoT是指窄带物联网(Na ...
- 推荐给 Java 程序员的 7 本书
< Java 编程思想> 适合各个阶段 Java 程序员的必备读物.书中对 Java 进行了详尽的介绍,与其它语言做了对比,解释了 Java 很多特性出现的原因和解决的问题.初学者可以通过 ...
- Java集合(类)框架(三)
1. Map集合 相较于List和Set集合而言,Map集合所储存的数据为双列行,数据是以key和value为一个单位进行存储的,如在建立一个学生Map的时候,其中的数据应为 学号-姓名(key-va ...
- Java入门(6)
阅读书目:Java入门经典(第7版) 作者:罗格斯·卡登海德 当方法在子类和超类都定义了时,将使用子类的定义:因此子类可以修改,替换或完全删除超类的行为或属性. 关键字super引用对象的上一级超类, ...
- leetcode134:3sum
题目描述 给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组. 注意: 三元组(a.b.c)中的元素必须按非降序排列.(即a≤b≤c) 解集中不能 ...
- Core WebApi项目快速入门(二):Filter详解
Core的核心是DI,面向AOP的编程方式.在.NetCore中AOP的实现很大程度上依赖于Filter.下面就Core WebApi中的Filter进行举例. 其中鉴权中心我个人认为是比较重要的概念 ...
- 鸿蒙系统应用开发之JS实现一个简单的List
在之前的文章鸿蒙应用开发之怎么更好的远程连接手表模拟器做调试里我运行了一个穿戴设备的应用,利用JS UI实现了一个最简单的HelloWorld. 今天我打算在智慧屏设备上利用豆瓣音乐的接口数据实现一个 ...
- [MIT6.006] 5. Binary Search Trees, BST Sort 二分搜索树,BST排序
第5节课主要讲述了二分搜索树概念和BST排序.讲师提出一个关于"跑道预订系统"的问题,假设飞机场只有一个跑道,飞机需要为未来降落时间t进行预订,如果时间集合R中,在t时间前后k分钟 ...
- AI时代,还不了解大数据?
如果要问最近几年,IT行业哪个技术方向最火?一定属于ABC,即AI + Big Data + Cloud,也就是人工智能.大数据和云计算. 这几年,随着互联网大潮走向低谷,同时传统企业纷纷进行数字化转 ...