既然要读MyBatis的源码,那么我们就要先弄清楚MyBatis的入口在哪。这里我们直接写一个标准的MyBatis使用程序,以此来寻找入口分析源码:
//第一步:读取mybatis-config.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//第二步:构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//第三步:打开SqlSession
SqlSession session = sqlSessionFactory.openSession();
//第四步:获取Mapper接口对象
UUserInfoMapper uUserInfoMapper = session.getMapper(UUserInfoMapper.class);
//第五步:调用Mapper接口对象的方法操作数据库;
UUserInfo uUserInfo = uUserInfoMapper.selectByPrimaryKey(1);
//第六步:业务处理
log.info("查询结果: " + uUserInfo.getId() + "--" + uUserInfo.getPhone());
这个地方你替换成为这个也是可以的;
//第一步:读取mybatis-config.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//InputStream inputStream = App.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
或者
Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatisConfig.xml")
这个方法更加通用,在web环境和java环境下均可以使用
//第二步:构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
这个地方使用了建造者模式。build方法返回的实际上是一个DefaultSqlSessionFactory对象(实现了SqlSessionFactory)并持有一个Configuration的引用:
千层饼读法:
第一层:
第二层:
第三层:解析new XMLConfigBuilder(Reader reader, String environment, Properties props)方法
解析:
XPathParser
:XPathParser核心功能是封装了XPath,对表达式进行解析,并转化成为指定的数据类型,其属性如下;
private Document document;
private boolean validation;
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
所有构造函数做的事都如下所示:
public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(inputStream));
}
// 将InputSource对象转化为Document对象
private Document createDocument(InputSource inputSource) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation); // 是否要校验由外界传入的值确定
factory.setNamespaceAware(false); // 如果要使用mybatis的XSD Schema,此处必须设为true,但源码里是false,说明官方默认用dtd来做校验,舍弃了XSD Schema
factory.setIgnoringComments(true); // 默认忽略注释
factory.setIgnoringElementContentWhitespace(false); // 只有开启校验才能去除xml中的空白节点,但是不知是否开启校验,所以这里设为了false
factory.setCoalescing(false);
factory.setExpandEntityReferences(true); // 默认开启使用扩展实体引用
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver); // 使用传入的EntityResolver对象
builder.setErrorHandler(new ErrorHandler() { // 定义解析xml文档的错误处理器,如果发生了错误或致命错误则直接抛出异常,如果是警告默认不做处理
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {}
});
return builder.parse(inputSource); //开始解析
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
MyBatis解析配置文件的本质就是为了获得Configuration对象;
Configuration 对象可以理解是mybatis的XML配置文件在程序中的化身,是MyBatis非常重要的一个对象,里面封装了MyBatis的整个配置信息
DefaultSqlSessionFactory 类的有参构造需传入 Mybatis 核心配置类 Configuration 的对象作为参数,而 Configuration 内容很多,初始化比较麻烦,因此使用了专门的建造者 XMLConfigBuilder 进行创建
把源码看过一遍之后我们大概可以看出来建造者模式的使用结构:
其中在XMLConfigBuilder中的parseConfiguration方法中对Configuration的属性进行装配:
这里就对应了xml配置文件
通过以上源码,我们就能看出,在mybatis的配置文件中:
我们debug到这个方法,可以看到configuration对象的内部结构:
总结一下:
XMLConfigBuilder 类负责创建复杂对象 Configuration,可以视为具体建造者角色,而SqlSessionFactoryBuilder则是以封装式构建 SqlSessionFactory 实例,相当于简化的建造者模式。
第一层:
//第三步:打开SqlSession
SqlSession session = sqlSessionFactory.openSession();
第二层:
第三层:
configuration.getEnvironment():从configuration配置对象中获取环境environment信息
getTransactionFactoryFromEnvironment(environment):根据环境environment信息获取事务工厂TransactionFactory
transactionFactory.newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit):根据数据源、事务隔离级别、是否自动提交来创建事务。其本质就是new了一个JDBC事务
Executor Configuration.newExecutor(Transaction transaction, ExecutorType executorType):根据执行器类型和事务创建执行器Executor对象
DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit):根据configuration和Executor创建一个默认的DefaultSqlSession对象并返回。
然后我们来看看详细过程:
//第四步:获取Mapper接口对象
UUserInfoMapper uUserInfoMapper = session.getMapper(UUserInfoMapper.class);
List<Stu> stus = mapper.selectSome(1);
未完待续。。。