最近开始系统阅读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有诸多标签,如下图所示:
//## 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);
}
}
后续再详细分析每个标签的解析过程