mybatis是现在流行的持久层框架,现在这家公司也从之前的ssh项目慢慢转成了ssm项目,而我之前用过ibatis,ibatis正是mybatis的前身。故想找时间来阅读下其源码,但是每天能抽出来的时间有限,只能慢慢的记录下来。

源码调试

mybatis是一个开源的框架,所以我们直接从github上拉取代码,甚至可以fork到自己的仓库里,也方便自己在上面添加注释。
我使用IDEA开发工具拉取代码,版本为3.5.3。
拉取完成后可以找下org.apache.ibatis.autoconstructor.AutoConstructorTest

AutoConstructorTest是单元测试类,任意一个单元测试方法都可以直接开始调试。它使用的是HSQLDB。

开始阅读

1
2
3
4
5
6
7
8
9
10
11
@BeforeAll
static void setUp() throws Exception {
// create a SqlSessionFactory
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}

// populate in-memory database
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/autoconstructor/CreateDB.sql");
}

Resources.getResourceAsReader()是读取xml配置。
进入new SqlSessionFactoryBuilder().build(),会发现有很多build()是有很多重载的方法,但最终都会去执行build(Reader reader, String environment, Properties properties),所以直接看这个好了。
在此方法里,会创建一个XMLConfigBuilder,直接点进去看其构造函数。
它有很多构造函数,随便挑一个来看。

1
2
3
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}

这里有两个重要的对象,一个是XPathParserXMLMapperEntityResolver
XPathParser是mybatis基于XPath解析器,封装了XPath、Document和EntityResolver,用来解析配置文件mybatis-config.xml以及Mapper.xml,属于mybatis的基础支持层中的解析器模块。XMLMapperEntityResolverEntityResolver的实现类,核心是resolveEntity()方法,来读取DTD文档。DTD文件主要验证xml文件编写的合法性。
查看XPathParser的源码,首先它有很多构造函数,也随便挑一个来看,

1
2
3
4
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}

commonConstructor()为初始化XPathParser的几个参数。
createDocument()是将xml解析成Document对象。
在调用createDocument()方法前,一定要先调用commonConstructor()

回到XMLConfigBuilder构造函数中来。

1
2
3
4
5
6
7
8
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); //创建Configuration对象
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props); //设置Configuration对象的variables属性。
this.parsed = false;
this.environment = environment;
this.parser = parser;
}

接下来看看XMLConfigBuilder.parse()方法。

1
2
3
4
5
6
7
8
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

parsed表示xml是否已经解析过了 。
parseConfiguration()方法则是解析 XML中的configuration节点。
进入parseConfiguration()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void parseConfiguration(XNode root) {
try {
//解析properties标签
propertiesElement(root.evalNode("properties"));
//解析settings标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
//暂且不知道啥作用
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//解析typeAliases标签
typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins标签
pluginElement(root.evalNode("plugins"));
//解析objectFactory标签
objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
//解析environments标签
environmentsElement(root.evalNode("environments"));
//解析databaseIdProvider标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers标签
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

解析完xml返回Configuration对象。
最终SqlSessionFactoryBuilder.bulid()返回SqlSessionFactory对象。
至此第一部分代码执行结束,剩下的部分就比较简单了。

1
2
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/autoconstructor/CreateDB.sql");

sqlSessionFactory中获取到数据库连接,并执行指定路径下的sql文件,为测试类生成测试数据。

好了,以上就是是简单的加载mybatis-config文件流程。其中XMLConfigBuilder中还有一些方法,放到后面在看。




参考文献

  • Mybatis技术内幕