【mybatis】mybatis数据源源码剖析(JNDI、POOLED、UNPOOLED)
一、概述
二、创建
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为例,追踪一下源码。
- public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
- try {
- // mybatis-config.xml文件的解析对象
- // 在XMLConfigBuilder中封装了Configuration对象
- // 此时还未真正发生解析,但是将解析的必备条件都准备好了
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
- // parser.parse()的调用标志着解析的开始
- // mybatis-config.xml中的配置将会被解析成运行时对象封装到Configuration中
- return build(parser.parse());
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- ErrorContext.instance().reset();
- try {
- inputStream.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
在XMLConfigBuilder进一步追踪,疑问最终保留在其父类BaseBuilder的resolveClass方法上,该方法对数据源工厂的字节码进行查找。
- public Configuration parse() {
- if (parsed) {
- throw new BuilderException("Each XMLConfigBuilder can only be used once.");
- }
- parsed = true;
- // mybatis-config.xml的根节点就是configuration
- // 配置文件的解析入口
- parseConfiguration(parser.evalNode("/configuration"));
- return configuration;
- }
- private void parseConfiguration(XNode root) {
- try {
- propertiesElement(root.evalNode("properties")); //issue #117 read properties first
- typeAliasesElement(root.evalNode("typeAliases"));
- pluginElement(root.evalNode("plugins"));
- objectFactoryElement(root.evalNode("objectFactory"));
- objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
- settingsElement(root.evalNode("settings"));
- // environment节点包含了事务和连接池节点
- environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
- databaseIdProviderElement(root.evalNode("databaseIdProvider"));
- typeHandlerElement(root.evalNode("typeHandlers"));
- mapperElement(root.evalNode("mappers"));
- } catch (Exception e) {
- throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
- }
- }
- private void environmentsElement(XNode context) throws Exception {
- if (context != null) {
- if (environment == null) {
- // 如果调用的build没有传入environment的id
- // 那么就采用默认的environment,即environments标签配置的default="environment_id"
- environment = context.getStringAttribute("default");
- }
- for (XNode child : context.getChildren()) {
- String id = child.getStringAttribute("id");
- if (isSpecifiedEnvironment(id)) {
- TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
- // 数据源工厂解析
- // 这里是重点,数据源工厂的查找
- DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
- // 工厂模式,生成相应的数据源
- DataSource dataSource = dsFactory.getDataSource();
- Environment.Builder environmentBuilder = new Environment.Builder(id)
- .transactionFactory(txFactory)
- .dataSource(dataSource);
- configuration.setEnvironment(environmentBuilder.build());
- }
- }
- }
- }
- private DataSourceFactory dataSourceElement(XNode context) throws Exception {
- if (context != null) {
- // dataSource标签的属性type
- String type = context.getStringAttribute("type");
- // 解析dataSource标签下的子标签<property name="" value="">
- // 实际上就是数据源的配置信息,url、driver、username、password等
- Properties props = context.getChildrenAsProperties();
- // resolveClass:到XMLConfigBuilder的父类BaseBuilder中进行工厂Class对象的查找
- // 这里是重点
- DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
- factory.setProperties(props);
- return factory;
- }
- throw new BuilderException("Environment declaration requires a DataSourceFactory.");
- }
在父类中并没有窥探到重点,转到其实例属性typeAliasRegistry中才真正进行查找过程。
- protected Class<?> resolveClass(String alias) {
- if (alias == null) return null;
- try {
- // 做了一下检查,转
- return resolveAlias(alias);
- } catch (Exception e) {
- throw new BuilderException("Error resolving class. Cause: " + e, e);
- }
- }
- protected Class<?> resolveAlias(String alias) {
- // BaseBuilder中的实例属性
- // 实例属性:protected final TypeAliasRegistry typeAliasRegistry;
- return typeAliasRegistry.resolveAlias(alias);
- }<span style="font-family: SimSun; background-color: rgb(255, 255, 255);"> </span>
typeAliasRegistry中实际上是在一个Map中进行KV的匹配。
- public <T> Class<T> resolveAlias(String string) {
- try {
- if (string == null) return null;
- String key = string.toLowerCase(Locale.ENGLISH); // issue #748
- Class<T> value;
- if (TYPE_ALIASES.containsKey(key)) {
- // private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
- // TYPE_ALIASES是一个实例属性,类型是一个Map
- value = (Class<T>) TYPE_ALIASES.get(key);
- } else {
- value = (Class<T>) Resources.classForName(string);
- }
- return value;
- } catch (ClassNotFoundException e) {
- throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
- }
- }
那么问题就来了,工厂类什么时候被注册到这个map中的?
实际上在SqlSessionFactoryBuilder的build(InputStream, env, Propeerties)方法中调用parse解析配置文件之前,我们忽略了一段重要的代码。
查看创建XMLConfigBuilder的过程,根据继承中初始化的规则,将会在父类BaseBuilder构造方法中创建Configuration对象,而Configuration对象的构造方法中将会注册框架中的一些重要参数。
- public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
- // 转
- this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
- }
- private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
- // 转调父类构造方法
- // 同时最终要的是直接new Configuration()传入父类
- // Configuration中的属性TypeAliasRegistry将会注册数据源工厂
- super(new Configuration());
- ErrorContext.instance().resource("SQL Mapper Configuration");
- this.configuration.setVariables(props);
- this.parsed = false;
- this.environment = environment;
- this.parser = parser;
- }
- public abstract class BaseBuilder {
- protected final Configuration configuration;
- protected final TypeAliasRegistry typeAliasRegistry;
- protected final TypeHandlerRegistry typeHandlerRegistry;
- public BaseBuilder(Configuration configuration) {
- this.configuration = configuration;
- // typeAliasRegistry来自于Configuration
- // 也就是合理解释了刚才通过typeAliasRegistry来找数据源工厂
- this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
- this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
- }
至此,数据源创建结束。接下来就看看怎么用。
三、详解
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)的更多相关文章
- mybatis数据源源码剖析(JNDI、POOLED、UNPOOLED)
http://blog.csdn.net/reliveit/article/details/47325189
- Mybatis架构原理(二)-二级缓存源码剖析
Mybatis架构原理(二)-二级缓存源码剖析 二级缓存构建在一级缓存之上,在收到查询请求时,Mybatis首先会查询二级缓存,若二级缓存没有命中,再去查询一级缓存,一级缓存没有,在查询数据库; 二级 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- Mybatis主线流程源码解析
Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细,但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题,需要花费大量的时间解决. 抽空了解一下Mybat ...
- Mybatis实现数据的增删改查
Mybatis实现数据的增删改查 1.项目结构(使用maven创建项目) 2.App.java package com.GetcharZp.MyBatisStudy; import java.io.I ...
- Springboot中mybatis执行逻辑源码分析
Springboot中mybatis执行逻辑源码分析 在上一篇springboot整合mybatis源码分析已经讲了我们的Mapper接口,userMapper是通过MapperProxy实现的一个动 ...
- MyBatis框架——mybatis插入数据返回主键(mysql、oracle)
向数据库中插入数据时,大多数情况都会使用自增列或者UUID做为主键.主键的值都是插入之前无法知道的,但很多情况下我们在插入数据后需要使用刚刚插入数据的主键,比如向两张关联表A.B中插入数据(A的主键是 ...
- mybatis ---- 实现数据的增删改查
前面介绍了接口方式的编程,需要注意的是:在book.xml文件中,<mapper namespace="com.mybatis.dao.IBookDao"> ,命名空间 ...
- mybatis框架(6)---mybatis插入数据后获取自增主键
mybatis插入数据后获取自增主键 首先理解这就话的意思:就是在往数据库表中插入一条数据的同时,返回该条数据在数据库表中的自增主键值. 有什么用呢,举个例子: 你编辑一条新闻,同时需要给该新闻打上标 ...
随机推荐
- Unity3D模型制作规范[转]
本文提到的所有数字模型制作,全部是用3D MAX建立的模型,即使是不同的驱动引擎,对模型的要求基本是相同的.当一个VR模型制作完成时,它所包含的基本内容包括:场景尺寸.单位,模型归类塌陷.命名.节点编 ...
- Node.js之网游服务器实践
此文已由作者尧飘海授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 随着Node.js的不断发展与壮大,应用范围也越来越广泛,从传统的企业应用,到互联网使用,再到云计算的发展, ...
- STL - merge()
merge用来对两个有序容器进行合并.返回合并后存入容器中的元素的下一个位置的迭代器(可以认为是超尾). merge(v1.first(),v1.end(),v2.first(),v2.end(),r ...
- 51nod1127【尺取】
思路: 尺取,写挫了,debug了半天. #include <bits/stdc++.h> using namespace std; typedef long long LL; const ...
- 3dmax学习资料记录
max2015 官方文档 http://help.autodesk.com/view/3DSMAX/2015/CHS/?guid=GUID-D015E335-EFB3-43BF-AB27-C3CB09 ...
- React的深入浅出
react组件重新渲染有两种途径:1.自身调用setState:2.父组件传入新的props.3.但这两种途径都不会必然调用render而引起重新渲染, 都会先经过shouldComponentUpd ...
- smtplib报警模块
#!/usr/bin/env python # coding:utf-8 import time import subprocess import smtplib from email.mime.te ...
- 3-zookeeper应用场景
1 概述 zk的核心体系是一个由业务注册进来的文件系统+对文件系统变化进行监听通知的监听机制. 假如在一个分布式系统中,有5台服务器,上面跑业务进程.在进程启动时,会去zk注册临时节点,并注册监听器. ...
- Charles对移动APP抓包(https)
1.下载安装Charles 2.设置代理 (1)查看默认端口:Proxy->Proxy Settings 在这个页面会看到HTTP Proxy的默认端口是8888 (2)查看当前电脑的IP:H ...
- 如何用Python在10分钟内建立一个预测模型
转载自:https://baijia.baidu.com/s?old_id=307995 最近,我从孙子(指<孙子兵法>——译者注)那里学到了一些策略:速度和准备 “兵之情主速,乘人之不及 ...