在“Mybatis入门”篇中,以一个简单的demo,让我们初步了解MyBatis的运行机制。
官方解释"每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得。而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先定制的configuration的实例构建SqlSessionFactory的实例"
下面跟踪源码查看构建SqlSessionFactory实例的过程。
1、创建SqlSessionFactory对象:
/**
* 获取 SqlSessionFactory
* Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互
* @return
*/
private SqlSessionFactory getSqlSessionFactory(){
/**
* MyBatis的配置文件
*/
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = null;
InputStream stream = null;
try {
/**
* 将Mybatis配置文件转成InputStream字节流,或者转成Reader字符流,两种方式MyBatis都支持
*/
stream = Resources.getResourceAsStream(resource);
/**
* 根据SqlSessionFactoryBuilder()创建SqlSessionFactory
*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
}catch (Exception e){
e.printStackTrace();
}
return sqlSessionFactory;
}
* 获取 SqlSessionFactory
* Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互
* @return
*/
private SqlSessionFactory getSqlSessionFactory(){
/**
* MyBatis的配置文件
*/
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = null;
InputStream stream = null;
try {
/**
* 将Mybatis配置文件转成InputStream字节流,或者转成Reader字符流,两种方式MyBatis都支持
*/
stream = Resources.getResourceAsStream(resource);
/**
* 根据SqlSessionFactoryBuilder()创建SqlSessionFactory
*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
}catch (Exception e){
e.printStackTrace();
}
return sqlSessionFactory;
}
2、首先我们看一下SqlSessionFactoryBuilder()是如何构建SqlSessionFactory对象的。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @return
*/
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @param environment 环境
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @param properties properties属性设置
* @return
*/
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
/**
*
* @param inputStream MyBatis配置文件字节流
* @param environment MyBatis 可以配置环境 如果忽略了环境参数,那么默认环境将会被加载。后面我们会详细讲到
* @param properties properties设置,从此处可以看出,mybatis配置文件中可以不配置properties。这也是我前面所说的第三种配置properties的方式,优先级最高
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
/**
*使用XMLConfigBuilder解析字节流,创建XMLConfigBuilder对象
*/
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.
}
}
}
/**
* 创建SqlSessionFactory 对象
* @param config XMLConfigBuilder对象
* @return
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @return
*/
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @param environment 环境
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
/**
*
* @param inputStream 将MyBatis的配置文件流传入构造方法中
* @param properties properties属性设置
* @return
*/
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
/**
*
* @param inputStream MyBatis配置文件字节流
* @param environment MyBatis 可以配置环境 如果忽略了环境参数,那么默认环境将会被加载。后面我们会详细讲到
* @param properties properties设置,从此处可以看出,mybatis配置文件中可以不配置properties。这也是我前面所说的第三种配置properties的方式,优先级最高
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
/**
*使用XMLConfigBuilder解析字节流,创建XMLConfigBuilder对象
*/
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.
}
}
}
/**
* 创建SqlSessionFactory 对象
* @param config XMLConfigBuilder对象
* @return
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
SqlSessionFactoryBuilder对象创建SqlSessionFactory,我主要解释一下字节流,字符流可参考字节流。
3、创建SqlSessionFactory 对象,主要是通过XMLConfigBuilder对象来解析MyBatis配置文件,下面我们了解一下解析过程。
XMLConfigBuilder类源码:
/**
* 解析XML配置文件
* @param inputStream
* @param environment
* @param props
*/
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
/**
* super(new Configuration());
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); 配置MyBatis的内建别名
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); 配置MyBatis默认的类型处理器 后面详细解释
*/
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
/**
* 在SqlSessionFactoryBuilder对象中创建完成XMLConfigBuilder对象时,调用此方法 return build(parser.parse());
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 此方法就是解析MyBatis配置文件configuration节点及子节点
* 这里表明configuration节点下面,可以配置如下节点
* @param root
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
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);
}
}
* 解析XML配置文件
* @param inputStream
* @param environment
* @param props
*/
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
/**
* super(new Configuration());
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); 配置MyBatis的内建别名
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); 配置MyBatis默认的类型处理器 后面详细解释
*/
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
/**
* 在SqlSessionFactoryBuilder对象中创建完成XMLConfigBuilder对象时,调用此方法 return build(parser.parse());
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 此方法就是解析MyBatis配置文件configuration节点及子节点
* 这里表明configuration节点下面,可以配置如下节点
* @param root
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
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);
}
}
4、通过上面的XMLConfigBuilder 类,构建Configuration对象,最后DefaultSqlSessionFactory(config)创建SqlSessionFactory对象。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
}
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
}
对于如何创建SqlSessionFactory对象,源码到此结束。
本人初入行不久,望大神们不吝赐教。