Mybatis源码解析之配置加载

Posted 叶长风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis源码解析之配置加载相关的知识,希望对你有一定的参考价值。

Mybatis源码解析之配置加载(一)

用了好几年的mybatis了,但是很少来钻研mybatis原理所在,最近抽出空来,就把这一整套源码都研究了下,然后发现就是这些东西,mybatis没啥难度,于是决定把研究的这一整套写一个mybatis系列,记录一下,在这些完了以后,顺便写一个小的mybatis框架。

1. demo演示


还是从用法开始吧,就不开始就从源码说了,mybatis的conf配置文件不再多讲,这里就是定义了一个domain,对应mapper文件,main程序,具体如下:

Domain

package cn.com.domain;

/**
 * @author xiaxuan
 * @date 2018/4/10.
 */
public class User 

    private Long id;
    private String username;
    private String password;
    private Integer age;
    private String mobile;
    private String hotelAddress;

   ....


Mapper

import cn.com.domain.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;

/**
 * @author xiaxuan
 * @date 2018/4/10.
 */
public interface UserMapper 

    User getUser(int id);

    @ResultMap("BaseResultMap")
    @Select("select id, username, password, age, mobile, hotel_address from user where id=#id;")
    User getUser2(@Param("id") Long id);

    void updateUser(@Param("age") Integer age, @Param("id") Long id);

    void save(User user);

    void deleteById(@Param("id") Long id);

xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.com.mapper.UserMapper">
    <!--
        映射数据库具体字段
        column: 数据库字段
        property: 对应实体类字段
     -->
    <resultMap id="BaseResultMap" type="cn.com.domain.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="mobile" property="mobile"/>
        <result column="hotel_address" property="hotelAddress"/>
    </resultMap>

    <!--
        根据id查询得到一个user对象
     -->
    <select id="getUser" parameterType="int" resultMap="BaseResultMap">
        select id, username, password, age, mobile, hotel_address from user where id=#id
    </select>

    <update id="updateUser">
        update user set age = #age WHERE id = #id;
    </update>

    <insert id="save">
        insert into user(username, password, age, mobile, hotel_address) values(#username, #password, #age, #mobile, #hotelAddress);
    </insert>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from user where id = #id;
    </delete>
</mapper>

想了想还是把conf文件也贴出来,因为等会分析时需要对照conf文件来分析,conf如下:

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <!--
                配置数据库连接信息
                连接池保存的是连接mysql的数据库连接
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <!--
                    连接mysql的网络地址
                    jdbc:mysql://localhost:3306/mybatis
                    jdbc:mysql: 协议
                    localhost: 地址 本地指向127.0.0.1
                    3306: 端口
                    mybatis:数据库名
                 -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="cn/com/mapper/userMapper.xml"/>
    </mappers>

</configuration>

main程序如下:

public class Main 

    public static void main(String[] args) 
        //mybatis的配置文件
        String resource = "conf.xml";
        //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
        InputStream is = Main.class.getClassLoader().getResourceAsStream(resource);
        //构建sqlSession的工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sessionFactory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //执行查询返回一个唯一user对象的sql
        User user = userMapper.getUser(1);
        System.out.println(user);

        User user2 = userMapper.getUser2(1L);
        System.out.println(user2);

        userMapper.updateUser(23, 1L);

        User user3 = userMapper.getUser2(1L);

        System.out.println(user3);

        session.close();
    


以上就是一个demo的完整演示,但是运行结果就不再贴出来了,毕竟不是使用指南,下面就就上面的演示程序具体讲解mybatis源码。

2.初始化配置分析


接下来开始对mybatis源码进行分析与讲解,首先说下配置文件是如何加载的,这里最主要的就是上图main程序中的前几行代码,如下:

//mybatis的配置文件
        String resource = "conf.xml";
        //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
        InputStream is = Main.class.getClassLoader().getResourceAsStream(resource);
        //构建sqlSession的工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

将xml配置文件转换成stream流后,解析stream生成SqlsesssionFactory对象,进入到build源码中,如下:

  public SqlSessionFactory build(InputStream inputStream) 
    return build(inputStream, null, null);
  

  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.
      
    
  

这里的XMLConfigBuilder无非是把stream转换成XML解析对象,这一步跳过,重点在于**parser.parse()**方法,进入到其方法中。

public Configuration parse() 
    if (parsed) 
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  

parseConfiguration(parser.evalNode("/configuration"))这行中,首先获得了configuration节点,这个就对应上了上面配置文件conf.xml中的configuration配置,在获取configuration节点后开始解析configuration中内容,进入到parseConfiguration方法。

private void parseConfiguration(XNode root) 
    try 
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(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);
    
  

这里开始就是对各种配置的解析,后面对propertiesenvironments、甚至是重点的mappers的配置解析太多,单独放在这一篇里面未免篇幅过长,我这把配置解析分成两部分,这一部分源码讲解放在下一篇中。

以上是关于Mybatis源码解析之配置加载的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis 源码分析 - 配置文件解析过程

Mybatis之Mapper调用源码分析

mybatis源码解析之Configuration加载

mybatis源码配置文件解析之四:解析plugins标签

mybatis源码-解析配置文件之配置文件Mapper解析

mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)