目录

探究Configuration

探究parse方法

启动断点调试

过程

总结


当我们上手mybatis时,对于mybatis如何读取xml配置文件,获取SQLSessionFactory的底层源码还是处于一个比较模糊的状态,作者本人也是比较懵,所以本文尝试一下读一下SQLSessionFactoryBuilder的build方法源码,探索一下mybatis如何初始化数据连接以及读取配置文件的。

首先我们拿出一段正常mybatis执行sql语句的基本流程

@Testpublic void test1() throws IOException {//定义mybatis核心配置文件在classes下的路径String mybatisPath = "mybatisConfig.xml";//根据路径获取字节输入流对象InputStream is = Resources.getResourceAsStream(mybatisPath);//创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();//通过SqlSessionFactoryBuilder对象的build方法创建SqlSessionFactory对象SqlSessionFactory factory = sqlSessionFactoryBuilder.build(is);//通过工厂类的openSession方法获取sql执行对象SqlSession sqlSession = factory.openSession();//sql执行对象的selectOne方法//获取第一个参数,即执行id:由namespace + . + SQL语句标签idString id = "com.ling.mybatis.dao.UserDao" + "." + "findUserById";User user = sqlSession.selectOne(id);System.out.println(user);//关闭sqlSessionsqlSession.close();}

我们在使用build方法获取SQLSessionFactory对象时,方法传入了字节输入流,那么此时传入的字节输入流一定有着他的意义。我们向上看,字节输入流是我们读取好的mybatis中心配置文件,里面配置了与数据库连接有关的driver,url,username,password等信息,同时还配置了各个dao层配置sql的xml配置文件地址,那么这时我们心理有数了:这步实际上完成了数据文件的读取操作。

按住Ctrl点击build方法进入源码。

    public SqlSessionFactory build(InputStream inputStream) {return this.build((InputStream)inputStream, (String)null, (Properties)null);}

首先光标默认停靠的位置就是上面的方法,但是此方法明显调用了本类重载的build方法,我们根据返回值类型SqlSessionFactory,以及方法参数,上下寻找,最终我们找到了方法内调用的真正执行的方法,如下:

    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;}

我们尝试着分析一下源码,该方法目的十分明确,第一步定义SQLSessionFactory的变量引用,然后将配置文件的字节流作为构造器参数,创建了一个XMLConfiguration的对象,对象名字仿佛就在疯狂的暗示:“看我看我!我就是负责处理XML配置文件的对象!”。最后调用了parse(英文是解析的意思)方法,该方法返回值:Configuration。调用完该方法后就给引用赋上了值。那么我们可以断定,该方法就是将字节输入流中包含的信息读取出的方法。

探究Configuration

很明显,将一个InputStream传入对象构造器,然后调用对象parse解析方法得到一个Configuration对象,就是把字节码信息转化成了对象的信息。我们此时可以大胆的猜测,该对象处理了xml配置文件,利用对象的属性存储了配置文件信息,并且使用parse方法进行解析。我们先看看这个对象有什么属性。

他有四个成员变量,environment是环境,字符串类型,那么它会不会存储了一些信息呢?这个我也不明白,所以只能留个悬念了以后遇到了再解决了。

public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private final XPathParser parser;private String environment;private final ReflectorFactory localReflectorFactory;
}

看不懂,哈哈哈哈,不要慌,我们还是看看他在构造方法中到底做了什么事。

下面是build方法中创建对象时调用的构造器,很明显他使用了其他构造器。参数我们都是一路跟过来的,所以只有InputStream不是null,其他两个是null,所以我们只需要关注InputStream即可,该构造器将InputStream传入了XPathParser对象中。具体做了什么我们先不看。

    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}

继续ctrl跟进,查看这个构造器里卖的是什么药。

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());this.localReflectorFactory = new DefaultReflectorFactory();ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}

原来如此,他首先继承了父类Configuration,这个对象顾名思义,就是配置对象,那么里面肯定用于存储一系列配置信息。跟进Configuration对象,看看有没有我们想要的东西(我们想要的东西也就是能够存储读取出来的配置文件信息的容器

点进去give me a jump,吓我一跳,但是我的嘴角微微上扬,因为我看到了一个很敏感的词Environment,也就是第一个属性,这叫环境,也就是我们在mybatis中心配置文件中配置时用的那个标签名,心中的期待一下子提高了不少,Ctrl进入Environment看看里面什么样。

public class Configuration {protected Environment environment;protected boolean safeRowBoundsEnabled;protected boolean safeResultHandlerEnabled;protected boolean mapUnderscoreToCamelCase;protected boolean aggressiveLazyLoading;protected boolean multipleResultSetsEnabled;protected boolean useGeneratedKeys;protected boolean useColumnLabel;protected boolean cacheEnabled;protected boolean callSettersOnNulls;protected boolean useActualParamName;protected boolean returnInstanceForEmptyRow;protected String logPrefix;protected Class<? extends Log> logImpl;protected Class<? extends VFS> vfsImpl;protected LocalCacheScope localCacheScope;protected JdbcType jdbcTypeForNull;protected Set<String> lazyLoadTriggerMethods;protected Integer defaultStatementTimeout;protected Integer defaultFetchSize;protected ResultSetType defaultResultSetType;protected ExecutorType defaultExecutorType;protected AutoMappingBehavior autoMappingBehavior;protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;protected Properties variables;protected ReflectorFactory reflectorFactory;protected ObjectFactory objectFactory;protected ObjectWrapperFactory objectWrapperFactory;protected boolean lazyLoadingEnabled;protected ProxyFactory proxyFactory;protected String databaseId;protected Class<?> configurationFactory;protected final MapperRegistry mapperRegistry;protected final InterceptorChain interceptorChain;protected final TypeHandlerRegistry typeHandlerRegistry;protected final TypeAliasRegistry typeAliasRegistry;protected final LanguageDriverRegistry languageRegistry;protected final Map<String, MappedStatement> mappedStatements;protected final Map<String, Cache> caches;protected final Map<String, ResultMap> resultMaps;protected final Map<String, ParameterMap> parameterMaps;protected final Map<String, KeyGenerator> keyGenerators;protected final Set<String> loadedResources;protected final Map<String, XNode> sqlFragments;protected final Collection<XMLStatementBuilder> incompleteStatements;protected final Collection<CacheRefResolver> incompleteCacheRefs;protected final Collection<ResultMapResolver> incompleteResultMaps;protected final Collection<MethodResolver> incompleteMethods;protected final Map<String, String> cacheRefMap;
}

啊,我的天,这是啥:id,dataSource,太熟悉了,这就是数据库初始化的时用到了DataSource,在学习Spring的时候DataSource估计诸位没少配过吧哈哈哈。

public final class Environment {private final String id;private final TransactionFactory transactionFactory;private final DataSource dataSource;
}

终于经过一层一层的向内挖,我们终于找到了存储信息的容器:来自XMLConfiguration父类的Configuration的Environment属性。阶段目的达到了,我们回到XMLConfiguration对象调用parse作为build方法参数的那一段。

探究parse方法

鼠标放到parse方法上面,我们发现该方法返回了一个Configuration类型的参数,ohhhhhh,我们此时就可以断定parse方法将字节输入流中的内容已经存放到Configuration对象中,解析读取阶段已经完成了。

此时我们就可以放心的进入parse源码,看看程序到底是怎么读取xml配置文件的。

一个简单的判断语句,如果成员变量是true的话抛异常,看来重点就在else了。他调用了parseConfiguration方法,并且直接返回了存储配置文件信息的configuration属性,那么该方法调用时就将字节流信息存储到configuration属性中,该方法就是核心方法,我看一下参数:evalNode方法,虽然我不知道是干嘛的,但是看见Node,咦,这不是结点吗!!?原来操作了文件中的节点,那究竟是什么结点呢?看后面的字符串:configuration结点。啊,这不是mybatis中心配置文件的根标签吗,原来在这开始解析文件的,我们看看parseConfiguration方法怎么解析的。

    public Configuration parse() {if (this.parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");} else {this.parsed = true;this.parseConfiguration(this.parser.evalNode("/configuration"));return this.configuration;}}

向里跟进,原来是一个控制中心方法,通过各种各样的处理方法最终完成了解析操作,我们可以看出该方法中调用了数个本类对象的方法,而方法的参数中字符串内容是按照顺序排布的mybatis中心配置文件的标签!解析xml配置文件的方法有很多,我们在javaweb学习阶段了解了dom4j解析,这个我们不需要做太多了解。我们知道了是XMLConfiguration中的parseConfiguration方法完成了解析。

    private void parseConfiguration(XNode root) {try {this.propertiesElement(root.evalNode("properties"));Properties settings = this.settingsAsProperties(root.evalNode("settings"));this.loadCustomVfs(settings);this.loadCustomLogImpl(settings);this.typeAliasesElement(root.evalNode("typeAliases"));this.pluginElement(root.evalNode("plugins"));this.objectFactoryElement(root.evalNode("objectFactory"));this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));this.reflectorFactoryElement(root.evalNode("reflectorFactory"));this.settingsElement(settings);this.environmentsElement(root.evalNode("environments"));this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));this.typeHandlerElement(root.evalNode("typeHandlers"));this.mapperElement(root.evalNode("mappers"));} catch (Exception var3) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);}}

接下来就是看一下究竟是不是该方法将解析的结果存储到XMLConfiguration的configuration属性中的。

启动断点调试

        在使用parseConfiguration方法前configuration的Environment属性是空的

执行完parseConfiguration方法后,environment属性就有值

此时的DataSource也有了信息

既然拿到了信息(存储在Configuration对象中,也就是parse方法的返回值),那么build方法就可以根据信息建立连接了。

我们在此回到调用parse方法那一块,进入build方法源码,看看解析完xml配置文件后,build方法拿到了信息做了什么。

得到以下代码。返回了一个SQLSessionFactory的实现类DefaultSQLSessionFactory,并且把配置文件相关信息交给了构造器。不行了,好奇心驱使我看一看这个构造器有没有对无辜的配置文件信息做些什么。

    public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

啊这,看来使我们多疑了,该构造器只是简单的给configuration属性完成了初始化过程。人家是清白的。

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}
}

到这里读取build文件源码过程就结束了,下面我们来做个总结吧~

过程

首先我们进入了build方法,发现build方法中创建了XMLConfiguration对象用于解析配置文件的操作。

然后我们了解了XMLConfiguration如果解析了文件,那么解析后的结果究竟放在了哪里,经过调查我们发现,XMLConfiguration继承了BaseBuilder的configuration成员变量,而configuration对象的environment属性就是用于存储中心配置文件有关的信息。

接着我们回到了刚进入build时的源码,查看parse,发现其返回值正是Configuration,跟进parse方法,发现其调用了parseConfiguration方法,参数为XNode root,很明显是根节点的意思,该方法就是用于读取mybatis中心配置文件的方法。该方法内调用了多个本类的方法,没有多余的操作,并且按照中心配置文件中的标签顺序依次调用了对应的解析方法,并将结果存储在成员变量configuration中。此时完成了读取解析字节输入流的操作,并将字节码信息转化为对象属性。

最后我们查看了build方法,发现他只是new了一个SQLSessionFactory的实现类DefaultSQLSessionFactory,并且将configuration属性赋值到DefaultSQLSessionFactory的configuration属性上。

总结

SQLSessionFactoryBuilder功能:

1. 解析字节输入流对应mybatis中心配置文件

 核心方法:XMLConfiguration的parse方法

方法作用:把字节输入流中的信息通过parseConfiguration方法解析到XMLConfiguration对象的configuration属性中,并且将该属性作为返回值返回。

2.创建SQLSessionFactory的实现类对象DefaultSQLSessionFactory

                注意点:DefaultSQLSessionFactory中存储了配置文件信息。

Mybatis中,SQLSessionFactoryBuilder使用build方法时做了哪些事?相关推荐

  1. MyBatis中特殊符号处理方法汇总

    mybatis框架是java web开发必备的框架,很多mybatis新手写代码的时候,需要在sql中使用到大于等于,这时候如果像sql中一样直接使用了>.<.>=.<=,在运 ...

  2. Mybatis中SqlSessionFactoryBuilder().build() 不可以传入InputStream字节输入流

    一.问题: 不知道你是不是遇到了以下问题: 二.解决办法 1.其中一个解决办法是,不调用Resources.getResourceAsStream(Mybaits依赖包提供),改为调用Resource ...

  3. Mybatis中 Integer 值为0时,默认为空字符串的解决办法

    问题 需求是查询级别为0的用户 User对象里的level字段的值为0,查询时居然没有查到为level为0的用户. <select id="selectSelective" ...

  4. Mybatis中mapper接口里方法重载的实现

    看了网上的很多文章,说mapper接口里不能写重载方法,感觉这种说法不对,mapper接口是可以实现重载方法的. 实现方法 例如: package mapper;import pojo.User;im ...

  5. mybatis中查询字段为空时需要赋值_当数据表中的列和需要返回的对象的属性不完全一致, MyBatis是不会自动赋值的。此时,就可以使用【】元素进行处理。...

    [判断题]在 Windows7中,回收站与剪贴板一样,是内存中的一块区域.(1.0分) [填空题]在 Windows7中,若要选定多个连续的文件时,可先单击要选定的第一个文件,然后按下()键,再单击最 ...

  6. mybatis中使用if标签判断时,如果判断的字段是boolean(数据库bit)时,传的值是0或者false时,mybatis会把它变成空,相当于没有传值

    在mapper中的查询语句 <select id="sectionSelect" parameterType="com.yinpeng.model.QueryFli ...

  7. MyBatis中的collection使用方法

    demo: 实体:Position @TableName("tb_position") public class Position {private static final lo ...

  8. qt tabwidget怎么在切换到特定界面时做特定的事_自然用户界面——是什么以及如何设计更加自然的用户界面?...

    使用触摸.手势或语言等一系列方式的用户交互界面被称作自然用户界面(Natural User Interfaces,NUI).我们认为这些界面很容易使用所以会认为他们是自然的.可是界面自然有什么意义呢? ...

  9. qt tabwidget怎么在切换到特定界面时做特定的事_TapTap超3700个玩家评8.9分,这款“二次元+PVP”的动作游戏怎么就凉了?...

    文 | 手游那点事 | Tony.Ben "一周游戏看点"是手游那点事推出的一周游戏洞察栏目. 最近,一款在玩家群体中口碑颇为突出的二次元产品引起了小编注意,这款名为<神位纷 ...

最新文章

  1. 10玩rust_有趣的 Rust 类型系统: Trait
  2. 如何解决 未在本地计算机上注册“Microsoft.Jet.OLEDB.4.0” 提供程序
  3. git merge --squash改写提交
  4. OAM Kubernetes 标准实现与核心依赖库发布 | 云原生生态周报 Vol. 52
  5. python周志_Python学习周志—第一周(入门知识)
  6. sqoop 1.4.5 增量导入hive 0.12.0
  7. IDEA快捷键及使用技巧
  8. 如何在微信公众帐号开发模式下,通过程序代码向用户发送符号表情。
  9. Python风格总结:ASCII码与字符相互转换
  10. 卸载VS2005不完全出现的安装问题
  11. 计算机电路基础实验一仪器使用,计算机电路基础(1)实验.pdf
  12. 前端实现html转pdf方法总结
  13. 谈刺蛇c语言程序,C语言程设计实验内容与答案.doc
  14. excel下拉列表值的设定方式
  15. oracle SQL语句练习
  16. cadence17.2输出BOM清单
  17. 微信每天处理2.05亿通话 运营商只剩卖流量
  18. pytorch的cuda环境搭建(GPU版本安装)
  19. bzoj 2298 problem a
  20. U盘装系统及启动盘制作

热门文章

  1. java类的软考有哪些科目_软考各级别各科目考试分析
  2. DSPE-PEG-SH, 磷脂PEG巯基
  3. 使用ping测试MTU值
  4. 只有一行VNC server running on ’::1:5900' 没有其他输出
  5. QGIS/SwatEditor/QSWAT的安装
  6. 数据采集之贝壳新房【完整代码(数据库+图片)】
  7. Removing-Camera-Shake-from-a-Single-Photograph图像去模糊读书笔记
  8. 为计算机发展做出贡献的人的简介,【七十年七十人】郑守淇:为我国计算机事业奉献一生...
  9. openid php steam,Steam OpenID签名验证
  10. 广东省计算机二级c语言真题,广东省计算机二级考试题及答案