【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插入数据后获取自增主键 首先理解这就话的意思:就是在往数据库表中插入一条数据的同时,返回该条数据在数据库表中的自增主键值. 有什么用呢,举个例子: 你编辑一条新闻,同时需要给该新闻打上标 ...
随机推荐
- 010-- 开发脚本自动部署nginx_web和nfs及监控内存
1.编写脚本自动部署反向代理.web.nfs: #!/bin/bash #检测安装nginx function detection_nginx(){ if [ -f /etc/nginx/nginx. ...
- node-- express()模块
1.代码分析 var express = require('express'); var app = express(); app.get('/', function(req, res){ res.s ...
- 洛谷 - P2602 - 数字计数 - 数位dp
https://www.luogu.org/problemnew/show/P2602 第二道数位dp,因为“数位dp都是模板题”(误),所以是从第一道的基础上面改的. 核心思想就是分类讨论,分不同情 ...
- composer下载tp5第三方扩展
(谨记:如果使用 composer 命令安装失败,请查看根目录下的 composer.json 文件是否正确,并查看下的扩展是否有多个版本,下载的版本是否符合当前框架的版本) 1.基础 compose ...
- css为什么要定最小宽度和最大宽度,最小宽度和最大宽度有什么用
设最小和最大宽度,主要是为了防止页面变形而已如,如果一个页面的宽度设置为百分比,这时此页面的宽度会根据浏览器的宽度而定但如果浏览器的宽度过小,页面就会变形,例如,你做的用百分比设置的页面,而用户端的浏 ...
- mysql 如何创建一个简单的存储过程
1 用mysql客户端登入2 选择数据库 mysql>use test3 查询当前数据库有哪些存储过程 mysql>show procedure status where Db='test ...
- 转--oracle查看允许的最大连接数和当前连接数等信息
两个参数间的关系:sessions=1.1*processes+5 目前总结的语句,在查看数据的连接情况很有用,写完程序一边测试代码一边查看数据库连接的释放情况有助于分析优化出一个健壮的系统程序来. ...
- ogg 监控脚本
section 1: #! /bin/sh PATH=/usr/local/bin:$PATHORACLE_SID=statdb ORAENV_ASK=NO. oraenv > /dev/nul ...
- RabbitMQ九:远程过程调用RPC
定义 RPC(Remote Procedure Call Protocol)——远程过程调用协议:它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议 ...
- A8ERP管理系统(采购单管理)
花了一个星期的时间终于把采购模块完成了. 最近新开发的采购单管理,供大家参考学习,软件一步一步来.