【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插入数据后获取自增主键 首先理解这就话的意思:就是在往数据库表中插入一条数据的同时,返回该条数据在数据库表中的自增主键值. 有什么用呢,举个例子: 你编辑一条新闻,同时需要给该新闻打上标 ...
随机推荐
- HDU 1713 相遇周期 (最小公倍数)
题意:... 析:求周期就是这两个分数的最小公倍数,可以先通分,再计算分子的最小倍数. 代码如下: #pragma comment(linker, "/STACK:1024000000,10 ...
- 2019年5月22日 AY 程序员调侃语录
我是AY,杨洋,做wpf开发的,最近得了一种病,程序员患得患失综合征.同事说,我年纪在变大,技术跟不上.业余之间,我原创了写了一些语录,给大家中午休息,累疲惫的时候,开心放松下. 1.活着的每一天都无 ...
- poj3617【贪心】
题意: 给定长度为N的字符串S,要构造一个长度为N的字符串T串. 从S的头部删除一个字符,加到T的尾部 从S的尾部删除一个字符,加到T的尾部 目标是构造字典序尽可能小的字符串. 思路: 贪心,每次取小 ...
- Codeforces 快速竞技#4
快速竞技#4 A–Duff and Meat588A = =这题不知道怎么写题解了.. 直接上code---. #include<bits/stdc++.h> #include<st ...
- HDOJ3231醉
反正一开始就是瞎几把看题,然后题意理解了,什么飞机?只能去看题解了. 呵呵,可惜,题解看了三个小时,还是一知半解,先写了. - -菜鸡超级详细题解,强行掰弯一波,等下再问问别人吧. OK,OK开始!! ...
- ASP.NET Core中间件计算Http请求时间
ASP.NET Core通过RequestDelegate这个委托类型来定义中间件 public delegate Task RequestDelegate(HttpContext context); ...
- Luogu P1542包裹快递【实数域二分】 By cellur925
题目传送门 题目中说:最大值最小,显然是一个二分答案嘛qwq. 我们二分的答案显然是速度,但是由于实际中每一段的速度可能不同,所以这里我们把所有段进行的速度都定为二分出的答案. 二分的步骤好说,只是c ...
- memcached原理及介绍
memcached是一种缓存技术,在存储在内存中(高性能分布式内存缓存服务器).目的 : 提速.(传统的都是把数据保存在关系型数据库管理系统既RDBMS,客户端请求时会从RDBS中读取数据并在浏览器中 ...
- selenium之webdriverAPI接口详解
1. 浏览器操作 driver.maximize_window() #最大化窗口driver.execute_script('window.scrollTo(0,0);') #滚动窗口到最上面driv ...
- Lightoj 1025 - The Specials Menu (区间DP)
题目链接: Lightoj 1025 - The Specials Menu 题目描述: 给出一个字符串,可以任意删除位置的字符,也可以删除任意多个.问能组成多少个回文串? 解题思路: 自从开始学dp ...