在 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
有哪些实现类,每个实现类的功能是什么,没有达到接口与实现的分离。