上篇博客Mybatis源码分析(1)—mybatis简单入门中,我们通过搭建Mybatis的Demo工程mybatisCode,大概了解了整个mybatis运行的过程,是通过SqlSessionFactoryBuilder类,利用配置文件的输入流,创建了SqlSessionFactory,最后创建了SqlSession会话去执行Sql查询信息,具体代码我们可以回顾一下:
本篇文章开始,我们依然围绕的Demo工程mybatisCode,从SqlSessionFactoryBuilder类出发,层层解析mybatis配置文件初始化的过程,可以使用下面简单的图来展示.
public class SqlSessionFactoryBuilder {
//通过字节流(InputStream)的方式构件SqlSessionFacotry
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//通过XMLConfigBuilder对象的parse()方法,解析mybatis-config.xml配置文件,获得全局Configuration对象,最后创建了SqlSessionFactory对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
}
}
}
//最终通过全局Configuration配置类,实例化SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
//DefaultSqlSessionFactory为SqlSessionFactory的实现类,既SqlSessionFactory为接口
return new DefaultSqlSessionFactory(config);
}
}
通过上面的源码,我们可以得到几个信息:
这里我们会产生以下几个问题:
接下来我们具体来分析XMLConfigBuilder对象及其源码;
public class XMLConfigBuilder extends BaseBuilder {
}
public abstract class BaseBuilder {
//全局的Configuration对象,使用final修饰,只能初始化一次
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
//带参构造方法,通过configuration对象初始化BaseBuilder
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
}
//通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
public class XMLConfigBuilder extends BaseBuilder {
//用来标记XMLConfigBuilder只能解析一次mybatis-config.xml的标识
private boolean parsed;
//xml文件解析器,使用final只能初始化一次
private final XPathParser parser;
//当前数据库环境
private String environment;
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
//XPathParser为xml解析器,把mybatis-config.xml流创建了XPathParser实例
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//初始化父类BaseBuilder中的全局 Configuration 对象
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;//标识
this.environment = environment;//此处因为我们只通过配置流实例化XMLConfigBuilder,因此environment=null
this.parser = parser;//实例化全局的xml解析器
}
}
//通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//parser.parse()开始解析mybatis-config.xml配置文件,配置文件信息注册到全局Configuration配置类中
return build(parser.parse());
public class XMLConfigBuilder extends BaseBuilder {
//解析mybatis配置文件
public Configuration parse() {
if (parsed) {
//每个XMLConfigBuilder只能解析一次mybatis-config.xml
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//XPathParser是xml文件的解析器,包括检测xml文件格式,默认解析configuration根节点
//通过解析mybatis-config.xml各个节点信息,注册到全局Configuration配置类中
parseConfiguration(parser.evalNode("/configuration"));
//返回全局的Configuration对象
return configuration;
}
//最后逐一解析mybatis-config.xml配置文件中的各个节点
private void parseConfiguration(XNode root) {
try {
//解析节点
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);
}
}
}
//context此时为节点信息
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//获取节点属性default的值,既environment=mysql,具体可参照上面配置文件截图
environment = context.getStringAttribute("default");
}
//遍历节点的所有子节点
//根据配置文件,子节点目前只有一个
for (XNode child : context.getChildren()) {
//获取子节点中的属性id值为mysql
String id = child.getStringAttribute("id");
//判断子节点属性id的值跟父节点属性default是否一致,一致则当前的子节点信息作为mybatis的数据库环境
if (isSpecifiedEnvironment(id)) {
//获取的其子节点,实例化事务工厂对象
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//获取的其子节点,实例化数据源工程对象
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
//通过数据源工厂,获取数据源DataSource对象
DataSource dataSource = dsFactory.getDataSource();
//mybatis通过id=mysql实例化mysql的环境,把事务工厂和数据源赋予environmentBuilder构建者创建了mysql数据库环境
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//最后把mysql数据库环境注册到全局Configuration中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private boolean isSpecifiedEnvironment(String id) {
if (environment == null) {
throw new BuilderException("No environment specified.");
} else if (id == null) {
throw new BuilderException("Environment requires an id attribute.");
} else if (environment.equals(id)) {
return true;
}
return false;
}
下篇开始Mybatis源码分析(3)—配置节点properties源码解析,我们将对mybatis-config.xml配置文件中的各个节点,逐篇的分析,每个节点mybatis源码解析的过程,按照节点解析的顺序,我们优先解析节点properties