在 mybatis源码分析-环境搭建 一文中,我们的测试代码如下:
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> deptList = deptMapper.getAllDept();
System.out.println(deptList);
} finally {
sqlSession.close();
}
}
其中如何生成 InputStream
对象在 mybatis源码分析-配置文件加载
已经讲解。本次将探究 SqlSessionFactory
对象的生成,也就是下面这行代码执行了什么。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
首先上面那行代码,创建了一个 SqlSessionFactoryBuilder
对象,源码如下:
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
//省略其它代码
}
其次 SqlSessionFactoryBuilder
对象调用 build
方法,该方法源码如下:
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
这个方法看来有重载方法,我们继续看其重载方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
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.
}
}
}
这个方法主要关注:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
这个构造函数将我们的配置文件转换为 XMLConfigBuilder
对象,这里面的逻辑十分复杂, 我们暂且不深究。
return build(parser.parse());
这里有两处注意的地方, parser.parse()
返回一个 Configuration
对象,这个对象保罗万千, 暂时也不深究。
此外 build
方法的实现如下:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
它返回了 DefaultSqlSessionFactory
对象,并且将 Configuration
对象赋值其属性,有源码为证:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//省略其它代码
}
由于 DefaultSqlSessionFactory
实现了 SqlSessionFactory
接口,那么
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
就水到渠成了。
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
和
return build(parser.parse());
这两行代码的实现,因为它们太复杂了。目前只需要理解:
XMLConfigBuilder
对象 XMLConfigBuilder
对象的一个 parse
方法将 XMLConfigBuilder
对象转换为 Configuration
对象,而该对象是 DefaultSqlSessionFactory
必须要的属性。只有通过这个属性,才能实现 SqlSessionFactory
中定义的接口方法。 如果我们来实现上面的功能,一般人会怎么处理呢?
package com.yefengyu.mybatis;
public interface SqlSessionFactory {
void test();
}
package com.yefengyu.mybatis;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.session.Configuration;
import java.io.InputStream;
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration = null;
public DefaultSqlSessionFactory(InputStream inputStream) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);
this.configuration = parser.parse();
}
@Override
public void test() {
//使用 configuration 完成相关功能
}
}
package com.yefengyu.mybatis;
import org.apache.ibatis.io.Resources;
import java.io.IOException;
import java.io.InputStream;
public class Main {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(inputStream);
sqlSessionFactory.test();
}
}
这三段代码想模拟mybatis创建 SqlSessionFactory
对象的过程,比如 SqlSessionFactory
有一些接口 test
,其实现类 DefaultSqlSessionFactory
实现此方法需要 Configuration
对象,需要从 InputStream
通过构造函数传入并解析为 Configuration
对象。测试代码中直接使用 DefaultSqlSessionFactory
创建 SqlSessionFactory
对象。
SqlSessionFactory
实现的话,把 InputStream
转换为 Configuration
的过程在每个构造函数都会有。 SqlSessionFactory
有哪些实现类,每个实现类的功能是什么,没有达到接口与实现的分离。