从Mybatis源码到Spring动态数据源底层原理分析系列一、Mybatis初始化源码浅析

private Environment environment;

private Map mappedStatements;

}

class Environment {

private TransactionFactory transactionFactory;

private DataSource dataSource;

}

复制代码

非常清晰的映射关系, 一个环境里面包含了数据源以及事务工厂, 如果不是很理解事务工厂的用处, 可以先不用着急, 我们后面的文章会进行详细的分析, 在本小节, 只是为了告诉大家, 一个mybatis的配置文件, 最终其实是以java对象保存的, 而xml中的标签层级, 其实就是对应了对象中的属性层级

2.4、mapper文件分析

对于xxxMapper.xml这些文件, mybatis中通过mapper标签的namespace + select / update等标签中的id构成一个唯一标识(假设为statementId), 每一个sql标签以MappedStatement的对象的形式保存在Configuration对象中, 在上面的Configuration中, 以statementId -> MappedStatement 形成一个映射关系, 而MappedStatement中则存储了一个select等类型的标签中的所有内容, 比如parameterType, resultType, resultMap, sql等, 当我们在触发sql执行的时候, 即通过statementId找到对应的MappedStatement, 取出里面的sql来执行, 然后利用resulthandler以及resultType或者resultMap等信息对结果集进行封装映射, 最后返回

在日常的开发中, 我们面向接口编程, 会定义一个个的Mapper接口类, 当执行这些接口方法的时候, 代理对象会利用接口的全路径类名 + 接口方法名构成一个statementId, 进而获取到对应的MappedStatement, 然后从中提取sql, 执行sql, 最后将结果集利用MappedStatement中的保存的resultMap等信息映射成对应 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 的java对象

2.5、总结

mybatis-con.xml这样的配置文件, 以Configuration对象存储, 表示整个mybatis的配置文件, 里面包含了一个环境(当前mybatis中对应的数据源以及事务工厂)以及所有的Mapper文件中一个个sql标签解析出来后的MappedStatement对象, xml文件的层次对应了java对象中的属性层次, MappedStatement中包含了一个sql标签需要执行的所有信息, 包括sql、动态sql解析需要的数据以及通过jdbc查询到的结果集如果处理的信息

3、配置文件初始化源码分析


3.1、代码引入

public static void main(String[] args) {

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream( “mybatis-con.xml” ) );

SqlSession sqlSession = sqlSessionFactory.openSession();

List objectList = sqlSession.selectList(“com.fightzhong.mapper.CustomerMapper.selectRemarksById”, 12602611);

}

复制代码

通过上面三行代码, 我们完成了mybatis的初始化, 以及执行了上面mapper中的一个select语句, 可以看到, 通过将mybatis-con.xml这个配置文件以流的形式读取, 然后利用这个文件流开始读取mybatis的配置, 我们可以先不用理会什么是SqlSession以及SqlSessionFactory, 在mybatis源码分析的最后, 我才会跟大家说明这两个东西是什么, 如果没有对底层依赖的组件有一个清晰的了解, 那么我们也不会深刻的了解到这两个类的真正作用! 不过SqlSessionFactoryBuilder.build方法却是我们需要进行分析的, 注意, 不要因为不知道SqlSessionFactoryBuilder和SqlSession是什么而感到烦恼!

3.2、build方法开始构建Configuration对象

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

return build(parser.parse());

}

复制代码

可以看到, build方法调用到最后, 就是创建了一个XMLConfigBuilder, 然后调用它的parse方法创建了Configuration对象而已, build方法的参数inputStream就是配置文件的文件流, environment字段就是表示我们期望生效标签中的哪个环境

3.3、parse方法开始构建Configuration对象

public Configuration parse() {

parseConfiguration(parser.evalNode(“/configuration”));

return configuration;

}

private void parseConfiguration(XNode root) {

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);

environmentsElement(root.evalNode(“environments”));

databaseIdProviderElement(root.evalNode(“databaseIdProvider”));

typeHandlerElement(root.evalNode(“typeHandlers”));

mapperElement(root.evalNode(“mappers”));

}

复制代码

非常清晰的xml解析过程, 最顶层解析configuration标签, 得到一个root(XNode), 如果有接触过xml解析, 或者有学过前端dom编程的同学就不会感到陌生, 就像前端的document.getxxx()方法的调用, 得到根节点后, 开始解析里面的一个个标签

properties: 解析properties标签, 将里面的一个个键值对取出来以Properties对象的形式保存到Configuration中

settings: 解析settings标签, 将一个个的mybatis配置设置到Configuration中, loadCustomVfs、 loadCustomLogImpl都是对settings标签的解析, settingsElement方法也是, 这个方法就是读取settings标签中的一个个配置, 然后调用configuration.setXXXX()方法进行设置

typeAliases: 别名的解析

plugins: mybatis插件的解析, 如果有使用过mybatis插件, 那么对于这里面的代码就会比较清晰, 其实就是一个个的拦截器而已, 大家有兴趣可以做一下mybatis插件的实践, 从而能够更加清楚的认识插件的作用

objectFactory: 在mybatis对sql执行完毕之后, 会对结果集进行处理, 创建一个个的对象并返回, 创建对象就是利用反射完成的, objectFactory即对象工厂, 完成对象反射的创建, 接口方法非常简单, 就是提供一个Class对象, 然后根据这个Class对象创建对应的实体类对象, 默认是DefaultObjectFactory, mybatis提供了扩展, 允许开发者自己定义对象创建的工厂类

objectWrapperFactory: 创建ObjectWrapper的工厂类, ObjectWrapper即对一个对象进行了包装, 然后利用该类中提供的反射方法对该对象进行反射属性的操作, 比如setXXX, getXXX (如果有了解过spring源码的话, 就会发现, 在spring创建bean对象的时候也有一个类似的功能类), 默认的实现类为DefaultObjectWrapperFactory, 里面没有任何功能, 该类是mybatis提供给开发者的扩展功能

reflectorFactory: ReflectorFactory工厂的解析, 用于创建Relector对象, 每个Relector对象中包含了一个class对象, 以及这个类中所有的set、get方法, set方法参数集合、get方法参数集合, 这个类其实就是反射工具类, 只需要提供一个对应的对象, 然后就能利用Relector完成反射方法的调用, 同样有默认的实现DefaultReflectorFactory, 其实ObjectWrapper最还是通过Reflector来完成反射方法的调用的

ObjectFactory、objectWrapper、Reflector是mybatis底层反射的核心功能类, 提供了完整的反射的功能, 我们这里仅仅是引入这些接口的功能而已, 因为在mybatis中可以通过配置文件提供自定义的实现类, 所以我们简单的提及一下, 大家有兴趣可以深入了解下这些类的功能

environments: 该标签就是解析当前mybatis中的数据源环境了, 完成了Environment对象的创建(同时完成了TransactionFactory和DataSource的创建, 前者是事务工厂, 用来创建事务的, 后面我们会详细分析,前面我们也有简单提到)

databaseIdProvider: 跳过, 不太清楚是干嘛的…

typeHandlers: 自定义typeHandlers的的解析, typeHandler是用于将jdbc类型和java类型进行映射的关键功能类, mybatis为目前java这边的绝大部分内置类型以及与对应的jdbc类型映射关系提供了默认的typeHandler, 存放在typeHandlerRegistry中, 该对象在初始化的同时会创建默认的typeHandler, typehandler作用在将java类型转成jdbc类型(即参数映射)或者将jdbc类型转成java类型(即结果集映射)中, 之前在项目中刚好有需要将数据库中以逗号分隔的字符串解析成一个List, 为了同时处理insert和select, 当时我采用了typeHandler完成了这个功能

mappers: 解析一个个的mapper文件, 将mapper文件中的一个个sql标签解析成MappedStatement对象并存储在Configuration中, 为了不影响主线, 解析mapper文件的代码我们不进行深入分析, 之后如果有机会再用文章来描述, 里面涉及到了ResultMap标签等的解析, 相对比较复杂

4、总结


我们从Mybatis配置文件、mapper文件出发, 引出了这些xml文件映射到java对象的整体情况, 随后对mybatis初始化代码进行了简单的分析, 其实就是将一个个的dom树解析成java对象来表示, 至此, 我们知道了Mybatis配置的整体模型:

class Configuration {

// 环境配置, 表示当前mybatis中生效的数据源对象以及事务创建的工具类

private Environment environment;

你可能感兴趣的:(Java,经验分享,架构,java)