mybatis-1.1-初始化过程

193 阅读2分钟

最近开始系统阅读mybatis源码,这篇笔记是关于mybatis的初始化过程分析

原生JDBC

现在已经很少在项目中直接使用原生JDBC去访问数据库,因为每一次访问都要重新获取数据库连接、手动拼接sql语句、访问结束后关闭数据库连接,频繁的数据库连接关闭操作很耗性能,并且原生JDBC的API使用起来也不方便

public class TestJdbc {
  static {
    try {
      Class.forName(Driver.class.getName());
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) throws Exception {
    Connection ct= DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8",
     "ct", "xxxx");
    PreparedStatement preparedStatement = ct.prepareStatement("select * from test where id=?");
    preparedStatement.setString(1,"1");
    ResultSet resultSet = preparedStatement.executeQuery();
    while (resultSet.next()) {
      String columnName1 = resultSet.getMetaData().getColumnName(1);
      String columnName2 = resultSet.getMetaData().getColumnName(2);
      System.out.println(columnName1+":"+resultSet.getString(1));
      System.out.println(columnName2+":"+resultSet.getString(2));
    }
    resultSet.close();
    preparedStatement.close();
    ct.close();
  }
}

mybatis初始化源码

从GitHub下载mybatis最新源码并导入到Idea,在test目录下创建测试代码进行调试:

//## Test.java
public class Test {
  public static void main(String[] args) throws Exception {
    String resource = "mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //line 7 打断点
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //通过jdk动态代理实现SQL语句执行
    DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("id","1");
    System.out.println(mapper.selectAll(map));
    sqlSession.close();
  }
}
//## DemoMapper.java
public interface DemoMapper {
  public List<Map<String,Object>>  selectAll(Map<String,Object> map);
}
// resources目录下的mapper包中的
//## DemoMapper.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="com.DemoMapper">
    <select  id="selectAll"   parameterType="Map" resultType="Map">
        select * from test WHERE id =#{id}
    </select>
</mapper>

mybatis初始化的基本流程:读取mybatis主配置文件后,new一个SqlSessionFactoryBuilder实例,调用其build方法,build方法主要的工作过程如下图所示。配置文件的inputStream作为方法输入参数,内部通过XMLConfigBuilder对象对配置文件的进行解析,最后返回SqlSessionFactory默认实现类DefaultSqlSessionFactory的实例

部分源码解析

开头main函数的build方法

//## org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //解析config.xml(mybatis解析xml是用的java dom)
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //parse(): 解析mybatis-config.xml里面的节点后返回Configuration对象
    return build(parser.parse());
  } catch (Exception e) {
    ...
  } finally {
    ...
  }
}

上面的parser.parse()方法

//## org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
public Configuration parse() {
  // parse方法只会调用一次,因为主配置文件的解析很耗费性能
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

configuration有诸多标签,如下图所示:

parseConfiguration方法就是逐一解析mybatis主配置文件中的各个标签

//## org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
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);
    // 解析alias,用一张map记录起来,key是alias,value是对应的Class
    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"));
    
    //解析mappers大标签下的每个mapper标签
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

后续再详细分析每个标签的解析过程