一、概述

二、创建

mybatis数据源的创建过程稍微有些曲折。

1. 数据源的创建过程;

2. mybatis支持哪些数据源,也就是dataSource标签的type属性可以写哪些合法的参数?

弄清楚这些问题,对mybatis的整个解析流程就清楚了,同理可以应用于任何一个配置上的解析上。

从SqlSessionFactoryBuilder开始追溯DataSource的创建。SqlSessionFactoryBuilder中9个构造方法,其中字符流4个构造方法一一对应字节流4个构造方法,都是将mybatis-config.xml配置文件解析成Configuration对象,最终导向build(Configuration configuration)进行SqlSessionFactory的构造。

配置文件的在build(InputStream, env, Properties)构造方法中进行解析,InputStream和Reader方式除了流不一样之外均相同,本处以InputStream为例,追踪一下源码。

  1. public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  2. try {
  3. // mybatis-config.xml文件的解析对象
  4. // 在XMLConfigBuilder中封装了Configuration对象
  5. // 此时还未真正发生解析,但是将解析的必备条件都准备好了
  6. XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
  7. // parser.parse()的调用标志着解析的开始
  8. // mybatis-config.xml中的配置将会被解析成运行时对象封装到Configuration中
  9. return build(parser.parse());
  10. } catch (Exception e) {
  11. throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  12. } finally {
  13. ErrorContext.instance().reset();
  14. try {
  15. inputStream.close();
  16. } catch (IOException e) {
  17. // Intentionally ignore. Prefer previous error.
  18. }
  19. }
  20. }

在XMLConfigBuilder进一步追踪,疑问最终保留在其父类BaseBuilder的resolveClass方法上,该方法对数据源工厂的字节码进行查找。

  1. public Configuration parse() {
  2. if (parsed) {
  3. throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  4. }
  5. parsed = true;
  6. // mybatis-config.xml的根节点就是configuration
  7. // 配置文件的解析入口
  8. parseConfiguration(parser.evalNode("/configuration"));
  9. return configuration;
  10. }
  11. private void parseConfiguration(XNode root) {
  12. try {
  13. propertiesElement(root.evalNode("properties")); //issue #117 read properties first
  14. typeAliasesElement(root.evalNode("typeAliases"));
  15. pluginElement(root.evalNode("plugins"));
  16. objectFactoryElement(root.evalNode("objectFactory"));
  17. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  18. settingsElement(root.evalNode("settings"));
  19. // environment节点包含了事务和连接池节点
  20. environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
  21. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  22. typeHandlerElement(root.evalNode("typeHandlers"));
  23. mapperElement(root.evalNode("mappers"));
  24. } catch (Exception e) {
  25. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  26. }
  27. }
  28. private void environmentsElement(XNode context) throws Exception {
  29. if (context != null) {
  30. if (environment == null) {
  31. // 如果调用的build没有传入environment的id
  32. // 那么就采用默认的environment,即environments标签配置的default="environment_id"
  33. environment = context.getStringAttribute("default");
  34. }
  35. for (XNode child : context.getChildren()) {
  36. String id = child.getStringAttribute("id");
  37. if (isSpecifiedEnvironment(id)) {
  38. TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
  39. // 数据源工厂解析
  40. // 这里是重点,数据源工厂的查找
  41. DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
  42. // 工厂模式,生成相应的数据源
  43. DataSource dataSource = dsFactory.getDataSource();
  44. Environment.Builder environmentBuilder = new Environment.Builder(id)
  45. .transactionFactory(txFactory)
  46. .dataSource(dataSource);
  47. configuration.setEnvironment(environmentBuilder.build());
  48. }
  49. }
  50. }
  51. }
  52. private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  53. if (context != null) {
  54. // dataSource标签的属性type
  55. String type = context.getStringAttribute("type");
  56. // 解析dataSource标签下的子标签<property name="" value="">
  57. // 实际上就是数据源的配置信息,url、driver、username、password等
  58. Properties props = context.getChildrenAsProperties();
  59. // resolveClass:到XMLConfigBuilder的父类BaseBuilder中进行工厂Class对象的查找
  60. // 这里是重点
  61. DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
  62. factory.setProperties(props);
  63. return factory;
  64. }
  65. throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  66. }

在父类中并没有窥探到重点,转到其实例属性typeAliasRegistry中才真正进行查找过程。

  1. protected Class<?> resolveClass(String alias) {
  2. if (alias == null) return null;
  3. try {
  4. // 做了一下检查,转
  5. return resolveAlias(alias);
  6. } catch (Exception e) {
  7. throw new BuilderException("Error resolving class. Cause: " + e, e);
  8. }
  9. }
  10. protected Class<?> resolveAlias(String alias) {
  11. // BaseBuilder中的实例属性
  12. // 实例属性:protected final TypeAliasRegistry typeAliasRegistry;
  13. return typeAliasRegistry.resolveAlias(alias);
  14. }<span style="font-family: SimSun; background-color: rgb(255, 255, 255);">  </span>

typeAliasRegistry中实际上是在一个Map中进行KV的匹配。

  1. public <T> Class<T> resolveAlias(String string) {
  2. try {
  3. if (string == null) return null;
  4. String key = string.toLowerCase(Locale.ENGLISH); // issue #748
  5. Class<T> value;
  6. if (TYPE_ALIASES.containsKey(key)) {
  7. // private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
  8. // TYPE_ALIASES是一个实例属性,类型是一个Map
  9. value = (Class<T>) TYPE_ALIASES.get(key);
  10. } else {
  11. value = (Class<T>) Resources.classForName(string);
  12. }
  13. return value;
  14. } catch (ClassNotFoundException e) {
  15. throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  16. }
  17. }

那么问题就来了,工厂类什么时候被注册到这个map中的?

实际上在SqlSessionFactoryBuilder的build(InputStream, env, Propeerties)方法中调用parse解析配置文件之前,我们忽略了一段重要的代码。

查看创建XMLConfigBuilder的过程,根据继承中初始化的规则,将会在父类BaseBuilder构造方法中创建Configuration对象,而Configuration对象的构造方法中将会注册框架中的一些重要参数。

  1. public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
  2. // 转
  3. this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  4. }
  5. private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  6. // 转调父类构造方法
  7. // 同时最终要的是直接new Configuration()传入父类
  8. // Configuration中的属性TypeAliasRegistry将会注册数据源工厂
  9. super(new Configuration());
  10. ErrorContext.instance().resource("SQL Mapper Configuration");
  11. this.configuration.setVariables(props);
  12. this.parsed = false;
  13. this.environment = environment;
  14. this.parser = parser;
  15. }
  1. public abstract class BaseBuilder {
  2. protected final Configuration configuration;
  3. protected final TypeAliasRegistry typeAliasRegistry;
  4. protected final TypeHandlerRegistry typeHandlerRegistry;
  5. public BaseBuilder(Configuration configuration) {
  6. this.configuration = configuration;
  7. // typeAliasRegistry来自于Configuration
  8. // 也就是合理解释了刚才通过typeAliasRegistry来找数据源工厂
  9. this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
  10. this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  11. }

至此,数据源创建结束。接下来就看看怎么用。

三、详解

1. Mybatis datasource结构

2. mybatis JNDI

mybatis JNDI之前已经剖析过源码,此处不再进行剖析,原文链接:点击打开链接

3. mybatis UNPOOLED

mybatis UNPOOLED数据源创建的思想,先通过默认构造方法创建数据源工厂(此时UNPOOLED dataSource随之创建),将mybatis-config.xml中数据源的配置信息通过setProperties传给工厂,然后通过工厂getDataSource。回顾一下这一段源码。

最终是利用简单的反射通过默认无参的构造方法实例化了数据源工厂,此时在数据源工厂中也实例化了UNPOOLED数据源对象。

未完待续!

【mybatis】mybatis数据源源码剖析(JNDI、POOLED、UNPOOLED)的更多相关文章

  1. mybatis数据源源码剖析(JNDI、POOLED、UNPOOLED)

    http://blog.csdn.net/reliveit/article/details/47325189

  2. Mybatis架构原理(二)-二级缓存源码剖析

    Mybatis架构原理(二)-二级缓存源码剖析 二级缓存构建在一级缓存之上,在收到查询请求时,Mybatis首先会查询二级缓存,若二级缓存没有命中,再去查询一级缓存,一级缓存没有,在查询数据库; 二级 ...

  3. mybatis 3.x源码深度解析与最佳实践(最完整原创)

    mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...

  4. Mybatis主线流程源码解析

     Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细,但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题,需要花费大量的时间解决. 抽空了解一下Mybat ...

  5. Mybatis实现数据的增删改查

    Mybatis实现数据的增删改查 1.项目结构(使用maven创建项目) 2.App.java package com.GetcharZp.MyBatisStudy; import java.io.I ...

  6. Springboot中mybatis执行逻辑源码分析

    Springboot中mybatis执行逻辑源码分析 在上一篇springboot整合mybatis源码分析已经讲了我们的Mapper接口,userMapper是通过MapperProxy实现的一个动 ...

  7. MyBatis框架——mybatis插入数据返回主键(mysql、oracle)

    向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...

  8. mybatis ---- 实现数据的增删改查

    前面介绍了接口方式的编程,需要注意的是:在book.xml文件中,<mapper namespace="com.mybatis.dao.IBookDao"> ,命名空间 ...

  9. mybatis框架(6)---mybatis插入数据后获取自增主键

    mybatis插入数据后获取自增主键 首先理解这就话的意思:就是在往数据库表中插入一条数据的同时,返回该条数据在数据库表中的自增主键值. 有什么用呢,举个例子: 你编辑一条新闻,同时需要给该新闻打上标 ...

随机推荐

  1. c++常见面试题30道

    1.new.delete.malloc.free关系 delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数.malloc与free是C++/C语言的标准库函数,new ...

  2. 2016 Multi-University Training Contest 2 A Acperience

    啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 题意: 略. 思路: 略....真分数... = =.我今天是纠结去死了.哎,继续加油,就比如gfd说的还有下一场,下下场,不要烦,不要绝望,因为,这算什么? )扔份代 ...

  3. hdoj2796

    题意: 1.在每一堆里顶部的coin的size必须大于这一堆其他的coin: 2.在每一堆里顶部的coin的size必须大于前面堆的顶部的coin: 3.在每一堆里顶部的coin的num必须大于前面堆 ...

  4. (四)SpringBoot如何定义消息转换器

    一:添加fastjson依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastj ...

  5. Python基础知识(1)

    Python 3 1:print:输出信息    例子:   ( 所有的标点符号都要是英文状态下输入,要不然会报错) print(“hello world”) 2:注意 : python 和 pyth ...

  6. nginx使用autoindex

    有时候一个nginx服务就是为了用来下载文件的,网上很多下载服务都是这样的 这个很简单 在http段加上以下参数,重启nginx就行. autoindex on; autoindex_exact_si ...

  7. 洛谷 P4317 花神的数论题 || bzoj3209

    https://www.lydsy.com/JudgeOnline/problem.php?id=3209 https://www.luogu.org/problemnew/show/P4317 设c ...

  8. UVa 1220 Party at Hali-Bula 晚会

    #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #i ...

  9. B. Connecting Universities DFS,无向树

    http://codeforces.com/problemset/problem/700/B 题意是,在一颗树中,有k个大学,要求两两匹配,他们之间的距离作为贡献,使得距离总和最大. 一开始的时候无从 ...

  10. ubuntu16.04里如何正确添加用root用户来登录图形界面(图文详解)

    不多说,直接上干货! Ubuntu版本都默认不允许使用root登录,必须要改配置文件. 第一步: 首先设置root密码,利用现有管理员帐户登陆Ubuntu,在终端执行命令:sudo passwd ro ...