`

Mybatis源码

 
阅读更多

一.

SqlSessionFactoryBuilder源码片段:

SqlSessionFactoryBuilder.build()主要参数类型包括(Reader reader,String environment,Properties properties)/(InputStream inputStream,String environment, Properties properties)

public class SqlSessionFactoryBuilder {

 

  //Reader读取mybatis配置文件,传入构造方法

  //除了Reader外,其实还有对应的inputStream作为参数的构造方法,

  //这也体现了mybatis配置的灵活性

  public SqlSessionFactory build(Reader reader) {

    return build(reader, null, null);

  }

 

  public SqlSessionFactory build(Reader reader, String environment) {

    return build(reader, environment, null);

  }

  

  //mybatis配置文件 + properties, 此时mybatis配置文件中可以不配置properties,也能使用${}形式

  public SqlSessionFactory build(Reader reader, Properties properties) {

    return build(reader, null, properties);

  }

  

  //通过XMLConfigBuilder解析mybatis配置,然后创建SqlSessionFactory对象

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {

    try {

      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

      //下面看看这个方法的源码

      return build(parser.parse());

    } catch (Exception e) {

      throw ExceptionFactory.wrapException("Error building SqlSession.", e);

    } finally {

      ErrorContext.instance().reset();

      try {

        reader.close();

      } catch (IOException e) {

        // Intentionally ignore. Prefer previous error.

      }

    }

  }

  

  

  //InputStream 读入

  public SqlSessionFactory build(InputStream inputStream) {

    return build(inputStream, null, null);

  }

 

  public SqlSessionFactory build(InputStream inputStream, String environment) {

    return build(inputStream, environment, null);

  }

 

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {

    return build(inputStream, null, properties);

  }

 

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

    try {

      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

      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.

      }

    }

  }

 

  public SqlSessionFactory build(Configuration config) {

    return new DefaultSqlSessionFactory(config);

  }

}

 

解析Mybatis主要用XMLConfigBuilder。

 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

 parser.parse();

 

 XMLConfigBuilder的源码:

 public class XMLConfigBuilder extends BaseBuilder {

 

  //Reader读取mybatis配置文件

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {

    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);

  }

  

  //InputStream读取mybatis配置文件

   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) {

    super(new Configuration());

    ErrorContext.instance().resource("SQL Mapper Configuration");

    this.configuration.setVariables(props);

    this.parsed = false;

    this.environment = environment;

    this.parser = parser;

  }

 

  // //外部调用此方法对mybatis配置文件进行解析

  public Configuration parse() {

    if (parsed) {

      throw new BuilderException("Each XMLConfigBuilder can only be used once.");

    }

    parsed = true;

////从根节点configuration

    parseConfiguration(parser.evalNode("/configuration"));

    return configuration;

  }

 

    //此方法就是依次解析configuration节点下的子节点

   //由此也可看出,我们在configuration下面能配置的节点为以下10个节点

  private void parseConfiguration(XNode root) {

    try {

      //issue #117 read properties first

      propertiesElement(root.evalNode("properties"));//read properties first

      typeAliasesElement(root.evalNode("typeAliases"));

      pluginElement(root.evalNode("plugins"));

      objectFactoryElement(root.evalNode("objectFactory"));

      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      reflectionFactoryElement(root.evalNode("reflectionFactory"));

      settingsElement(root.evalNode("settings"));

      // read it after objectFactory and objectWrapperFactory issue #631

      environmentsElement(root.evalNode("environments"));

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

    }

  }

 }

 

 <?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

 

<configuration> //从根节点configuration

 <settings>

<!--changes from the defaults -->

<setting name="cacheEnabled" value="false"/>

<setting name="lazyLoadingEnabled" value="false"/>

<setting name="mapUnderscoreToCamelCase" value="true"/>

<setting name="useColumnLabel" value="true"/>

<setting name="defaultExecutorType" value="REUSE"/>

<setting name="defaultStatementTimeout" value="25000"/>

<setting name="jdbcTypeForNull" value="NULL"/>

</settings>

</configuration>

 

通过以上源码,我们就能看出,在mybatis的配置文件中:

1. configuration节点为根节点。

2. 在configuration节点之下,我们可以配置10个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers。

 

 

二.properties节点和environments节点

<configuration>

 

<!-- 方法一: 从外部指定properties配置文件, 除了使用resource属性指定外,还可通过url属性指定url  

<properties resource="dbConfig.properties"></properties> 

 -->

 <!-- 方法二: 直接配置为xml -->

<properties>

 <property name="driver" value="com.mysql.jdbc.Driver"/>

 <property name="url" value="jdbc:mysql://localhost:3306/test1"/>

 <property name="username" value="root"/>

 <property name="password" value="root"/>

</properties>

 

   <!--environments节点 -->

<environments default="development">

<environment id="development">

 <transactionManager type="JDBC"/>

 <dataSource type="POOLED">

<!--

 如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置 

<property name="driver" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://localhost:3306/test1"/>

<property name="username" value="root"/>

<property name="password" value="root"/>

-->

 

<!-- 上面指定了数据库配置文件, 配置文件里面也是对应的这四个属性 -->

<property name="driver" value="${driver}"/>

<property name="url" value="${url}"/>

<property name="username" value="${username}"/>

<property name="password" value="${password}"/>

 

 </dataSource>

</environment>

 

<!-- 我再指定一个environment -->

<environment id="test">

 <transactionManager type="JDBC"/>

 <dataSource type="POOLED">

<property name="driver" value="com.mysql.jdbc.Driver"/>

<!-- 与上面的url不一样 -->

<property name="url" value="jdbc:mysql://localhost:3306/demo"/>

<property name="username" value="root"/>

<property name="password" value="root"/>

 </dataSource>

</environment>

</environments>

</configuration>

 

那么,外部指定properties配置文件,直接配置为xmlp这两种方法都同时用了,那么哪种方法优先?当以上两种方法都用xml配置优先, 外部指定properties配置其次。

 

environments元素节点可以配置多个environment子节点, 怎么理解呢? 

假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment,               两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 

将environments的deault属性的值配置为dev, 那么就会选择dev的environment。

 

XMLConfigBuilder对于properties和environments的解析:

public class XMLConfigBuilder extends BaseBuilder {

private boolean parsed;

//xml解析器

private XPathParser parser;

private String environment;

 

//上次说到这个方法是在解析mybatis配置文件中能配置的元素节点

//今天首先要看的就是properties节点和environments节点

private void parseConfiguration(XNode root) {

try {

 //优先解析properties元素 

 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"));

 //解析environments元素

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

}

}

 

 

//下面就看看解析properties的具体方法

private void propertiesElement(XNode context) throws Exception {

if (context != null) {

 //将子节点的 name 以及value属性set进properties对象

 //这儿可以注意一下顺序,xml配置优先, 外部指定properties配置其次

 Properties defaults = context.getChildrenAsProperties();

 //获取properties节点上 resource属性的值

 String resource = context.getStringAttribute("resource");

 //获取properties节点上 url属性的值, resource和url不能同时配置

 String url = context.getStringAttribute("url");

 if (resource != null && url != null) {

throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");

 }

 //把解析出的properties文件set进Properties对象

 if (resource != null) {

defaults.putAll(Resources.getResourceAsProperties(resource));

 } else if (url != null) {

defaults.putAll(Resources.getUrlAsProperties(url));

 }

 //将configuration对象中已配置的Properties属性与刚刚解析的融合

 //configuration这个对象会装载所解析mybatis配置文件的所有节点元素,以后也会频频提到这个对象

 //既然configuration对象用有一系列的get/set方法, 那是否就标志着我们可以使用java代码直接配置? 

 //答案是肯定的, 不过使用配置文件进行配置,优势不言而喻

 Properties vars = configuration.getVariables();

 if (vars != null) {

defaults.putAll(vars);

 }

 //把装有解析配置propertis对象set进解析器, 因为后面可能会用到

 parser.setVariables(defaults);

 //set进configuration对象

 configuration.setVariables(defaults);

}

}

 

//下面再看看解析enviroments元素节点的方法

private void environmentsElement(XNode context) throws Exception {

if (context != null) {

if (environment == null) {

//解析environments节点的default属性的值

//例如: <environments default="development">

environment = context.getStringAttribute("default");

}

//递归解析environments子节点

for (XNode child : context.getChildren()) {

//<environment id="development">, 只有enviroment节点有id属性,那么这个属性有何作用?

//environments 节点下可以拥有多个 environment子节点

//类似于这样: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments>

//意思就是我们可以对应多个环境,比如开发环境,测试环境等, 由environments的default属性去选择对应的enviroment

String id = child.getStringAttribute("id");

//isSpecial就是根据由environments的default属性去选择对应的enviroment

if (isSpecifiedEnvironment(id)) {

//事务, mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器, 

TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

//enviroment节点下面就是dataSource节点了,解析dataSource节点(下面会贴出解析dataSource的具体方法)

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

DataSource dataSource = dsFactory.getDataSource();

Environment.Builder environmentBuilder = new Environment.Builder(id)

 .transactionFactory(txFactory)

 .dataSource(dataSource);

//老规矩,会将dataSource设置进configuration对象

configuration.setEnvironment(environmentBuilder.build());

}

}

}

}

 

//下面看看dataSource的解析方法

private DataSourceFactory dataSourceElement(XNode context) throws Exception {

if (context != null) {

//dataSource的连接池

String type = context.getStringAttribute("type");

//子节点 name, value属性set进一个properties对象

Properties props = context.getChildrenAsProperties();

//创建dataSourceFactory

DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();

factory.setProperties(props);

return factory;

}

throw new BuilderException("Environment declaration requires a DataSourceFactory.");

}

 

在配置dataSource的时候使用了 ${driver} 这种表达式, 这种形式是怎么解析的?其实,是通过PropertyParser这个类解析:

/**

 * 这个类解析${}这种形式的表达式

 */

public class PropertyParser {

 

  public static String parse(String string, Properties variables) {

    VariableTokenHandler handler = new VariableTokenHandler(variables);

    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);

    return parser.parse(string);

  }

 

  private static class VariableTokenHandler implements TokenHandler {

    private Properties variables;

 

    public VariableTokenHandler(Properties variables) {

      this.variables = variables;

    }

 

    public String handleToken(String content) {

      if (variables != null && variables.containsKey(content)) {

        return variables.getProperty(content);

      }

      return "${" + content + "}";

    }

  }

}

分享到:
评论

相关推荐

    MyBatis源码分析.pdf

    MyBatis源码分析 MyBatis是一款流行的Java持久层框架,提供了强大的数据库访问能力,本文将对MyBatis的源码进行深入分析,从而帮助读者更好地理解MyBatis的工作机理。 1. MyBatis入门 MyBatis是一款基于Java的...

    Mybatis源码分析.pdf

    总结起来,MyBatis源码分析涵盖了从配置加载到数据库操作的全过程,涉及到了配置解析、SQL执行、结果映射等多个关键环节,以及Executor、StatementHandler等核心组件。通过深入学习MyBatis的源码,开发者不仅可以...

    阿里巴巴P7架构师纯手工打造MyBatis源码——1小时解密底层源码.txt

    ### MyBatis源码解析——由阿里巴巴P7架构师纯手工打造 #### 一、前言 在现代软件开发过程中,持久层框架如MyBatis因其简单易用、灵活高效的特点而受到广泛欢迎。作为一款优秀的Java持久层框架,MyBatis通过SQL...

    mybatis源码分析视频

    本资源“mybatis源码分析视频”是针对MyBatis框架进行深入剖析的教程,通过视频和文档的形式帮助学习者理解其内部工作机制。 1. **MyBatis简介** MyBatis消除了几乎所有的JDBC代码和手动设置参数以及获取结果集。...

    MyBatis源码详解学习.zip

    《MyBatis源码详解学习》是一份专为对MyBatis源码感兴趣的开发者准备的资料,它旨在帮助读者深入理解这个流行持久层框架的工作原理。MyBatis作为一个轻量级的ORM(对象关系映射)框架,因其简单易用、高度可定制化的...

    mybatis源码分析思维导图.rar

    MyBatis是一款流行的Java...总的来说,MyBatis源码分析思维导图会涵盖MyBatis的各个关键组件、工作流程、特性以及扩展机制。通过深入学习和理解这些内容,开发者能够更好地利用MyBatis进行数据库操作,并优化其性能。

    Mybatis源码学习-代码+笔记

    在深入学习Mybatis源码的过程中,我们可以了解到它的工作原理,更好地优化数据库交互,提高程序性能。 1. **Mybatis概述** Mybatis 源码的学习可以帮助开发者理解其内部机制,包括动态SQL的解析、SQL映射文件与...

    mybatis源码jar包

    在深入探讨MyBatis源码之前,我们首先需要理解MyBatis的基本概念和功能。MyBatis消除了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的POJOs...

    5套完整springboot+mybatis源码下载只供学习之用.rar

    通过学习这些源码,你可以深入理解SpringBoot如何启动、如何配置自动装配,以及MyBatis如何与数据库交互。同时,Shiro的使用也能帮助你掌握用户权限的控制。对于初学者,这是一个很好的实践平台,对于有经验的开发者...

    mybatis源码+配置步骤+包文件

    一、MyBatis源码解析 MyBatis的源码主要由SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和Mapper接口等核心组件构成。SqlSessionFactoryBuilder用于创建SqlSessionFactory,它是MyBatis的核心工厂类。...

    mybatis源码笔记.pdf

    阅读mybatis源码所记笔记。 基本: 1.数据源获取 2.mapper的sql语句获取 3.怎么去获得到的resultSet 重点: 1.orm是什么:体现于resultSet的数据库类型和java类型的转换 2.mybatis的一级缓存原理(简单的ifelse判断...

    mybatis源码

    深入理解MyBatis源码,有助于我们了解其执行流程,比如:当执行一个Mapper接口的方法时,MyBatis是如何定位到对应的SQL,如何处理参数,如何映射结果,以及如何执行SQL并返回结果。这对于优化数据库操作、调试问题...

    mybatis源码和官方的中文文档

    2. **MyBatis源码解析**: `mybatis-2.3.5-sources.jar`包含了MyBatis的源代码,这对于我们理解框架内部工作流程极其有价值。例如,你可以看到`SqlSession`、`SqlSessionFactory`和`Mapper`接口的实现,以及如何...

    Spring整合Mybatis源码解析

    Spring整合Mybatis源码解析

    MyBatis 源码解析:通过源码深入理解 SQL 的执行过程 - GitChat

    MyBatis 源码解析:通过源码深入理解 SQL 的执行过程 抓下来打包成了HTML文件, 方便离线观看

    MyBatis源码解析

    MyBatis源码解析

    互联网轻量级SSM框架解密:Spring、Spring MVC、MyBatis源码深度剖析 .rar

    SSM框架,全称为Spring、Spring MVC和MyBatis,是Java开发中广泛使用的轻量级Web...提供的"互联网轻量级SSM框架解密:Spring、Spring MVC、MyBatis源码深度剖析.pdf"文档,无疑是一份宝贵的参考资料,值得深入研究。

Global site tag (gtag.js) - Google Analytics