# MyBatis源码-配置文件加载

这一节我们来看看 MyBatis 如何读取配置文件

# 介绍

MyBatis 如果集成 springboot 当中,可以进行 ymlproperties 进行相关配置,这里我们不基于 springboot 进行演示是为了减少一些干扰。我们使用原生的 MyBatis 配置形式进行源码的解读。

先看xml配置文件

# mybatis-config.xml 配置内容

<?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>
    <typeAliases>
        <package name="com.github.yeecode.mybatisdemo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/yeecode?serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/github/yeecode/mybatisdemo/UserMapper.xml"/>
    </mappers>
</configuration>

# 测试代码

public static void main(String[] args) {
        // 第一阶段:MyBatis的初始化阶段
        String resource = "mybatis-config.xml";
        // 得到配置文件的输入流
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 得到SqlSessionFactory
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);

        // 第二阶段:数据读写阶段
        try (SqlSession session = sqlSessionFactory.openSession()) {
            // 找到接口对应的实现
            UserMapper userMapper = session.getMapper(UserMapper.class);
            // 组建查询参数
            User userParam = new User();
            userParam.setSchoolName("Sunny School");
            // 调用接口展开数据库操作
            List<User> userList =  userMapper.queryUserBySchoolName(userParam);
            // 打印查询结果
            for (User user : userList) {
                System.out.println("name : " + user.getName() + " ;  email : " + user.getEmail());
            }
        }
    }

# 加载配置文件代码分析

通过上面测试代码看到,传入一个配置文件名称获取一个输出流。拿到输出流从而构建了一个 SqlSessionFactory 对象。 我们先来看看 MyBatis 如何读取配置文件的,读取配置文件是这句代码进行的 getResourceAsStream(resource, getClassLoaders(classLoader)), resource 就是配置文件的名称,点开这个方法一路点下去找到主要的源代码:

//首先我们先看下 classLoader的方法实现,加载配置文件默认是从下面5个classLoader来加载,如果我们没有指定具体的classLoader,
//那第一个classLoader就是空
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }

//classLoader 的值就是上面 getClassLoaders 方法的返回值 
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
      //resource 就是我们传递的配置文件名称 mybatis-config.xml
      //classLoader 是从哪个路径下加载配置文件,
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // 先尝试从传递的路径查找资源
        InputStream returnValue = cl.getResourceAsStream(resource);

        // 如果查找不到,加 '/' 再次尝试
        if (null == returnValue) {
          returnValue = cl.getResourceAsStream("/" + resource);
        }

        if (null != returnValue) {
          return returnValue;
        }
      }
    }
    return null;
  }

# 文件流创建配置类

上面已经获取到了配置文件的文件流,然后开始构建 SqlSessionFactory ,主要关注的是解析 xml 文件。详细的解析,请看下一节。

    // 得到SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

  //构建配置类
  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 Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //从configuration 节点开始解析 
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  //解析xml 各个节点 比如:mappers environments(数据源) typeAliases
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(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);
    }
  }

参考文章

  1. 《通用源码阅读指导书—MyBatis源码详解》
上次更新: 2024/11/5