Mybatis源码分析(2)---初始化配置文件
2019-04-17 18:10
621 查看
1. 背景
上篇博客Mybatis源码分析(1)—mybatis简单入门中,我们通过搭建Mybatis的Demo工程mybatisCode,大概了解了整个mybatis运行的过程,是通过SqlSessionFactoryBuilder类,利用配置文件的输入流,创建了SqlSessionFactory,最后创建了SqlSession会话去执行Sql查询信息,具体代码我们可以回顾一下:
2. MyBatis初始化过程
本篇文章开始,我们依然围绕的Demo工程mybatisCode,从SqlSessionFactoryBuilder类出发,层层解析mybatis配置文件初始化的过程,可以使用下面简单的图来展示.
- 类结构:
通过SqlSessionFactoryBuilder类的源码结构中,我们可以看出来,整个SqlSessionFactoryBuilder类都是用来创建SqlSessionFactory对象的,创建的方式大体分为三种:
第一种:读取配置文件mybatis-config.xml,解析成Inpustream流的方式来创建;
第二种:读取配置文件mybatis-config.xml,解析成Reader流的方式来创建;
第三种:通过配置Configuration类创建;
以下我们将根据Demo工程中的代码,使用Inpustream流的方式来进一步的窥探源码初始化的过程 - SqlSessionFactoryBuilder源码
这里我们分析使用字节流InputStream创建SqlSessionFactory 的源码,其他字符流Reader或者Configuration源码创建的原理基本一样,我们直接上源码
public class SqlSessionFactoryBuilder { //通过字节流(InputStream)的方式构件SqlSessionFacotry public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //通过XMLConfigBuilder对象的parse()方法,解析mybatis-config.xml配置文件,获得全局Configuration对象,最后创建了SqlSessionFactory对象 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { } } } //最终通过全局Configuration配置类,实例化SqlSessionFactory public SqlSessionFactory build(Configuration config) { //DefaultSqlSessionFactory为SqlSessionFactory的实现类,既SqlSessionFactory为接口 return new DefaultSqlSessionFactory(config); } }
通过上面的源码,我们可以得到几个信息:
- Inpustream流的build方式,最终调用的是build(InputStream inputStream, String environment, Properties properties)方法来创建SqlSessionFactory;
- build方法的内部构建了XMLConfigBuilder对象,来解析mybatis-config.xml配置文件信息,注册到全局Configuration配置类中;
- 最后通过SqlSessionFactory的实现类DefaultSqlSessionFactory和全局Configuration配置类实例化了SqlSessionFactory对象;
这里我们会产生以下几个问题:
- XMLConfigBuilder对象是什么,具体的作用是什么?
- 它是怎么解析mybatis-config.xml配置文件?
- 它是怎么获取到Configuration实例呢?
接下来我们具体来分析XMLConfigBuilder对象及其源码;
2.2 XMLConfigBuilder类源码解析 2.2.1 XMLConfigBuilder介绍- 具体作用:XMLConfigBuilder见名知意,它是用来解析XML配置文件的,在mybatis中它负责解析mybatis的XML配置文件,本文中它解析的就是mybatis-config.xml配置文件
- 类结构:
public class XMLConfigBuilder extends BaseBuilder { } public abstract class BaseBuilder { //全局的Configuration对象,使用final修饰,只能初始化一次 protected final Configuration configuration; protected final TypeAliasRegistry typeAliasRegistry; protected final TypeHandlerRegistry typeHandlerRegistry; //带参构造方法,通过configuration对象初始化BaseBuilder public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); } }
- XMLConfigBuilder 继承了父类 BaseBuilder 对象,BaseBuilder 维护了整个mybatis全局的 Configuration 对象(因为使用了final修饰,具体可看上面源码)
- 在创建XMLConfigBuilder 对象时,会初始化这个全局的 Configuration 对象。
//通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse());
public class XMLConfigBuilder extends BaseBuilder { //用来标记XMLConfigBuilder只能解析一次mybatis-config.xml的标识 private boolean parsed; //xml文件解析器,使用final只能初始化一次 private final XPathParser parser; //当前数据库环境 private String environment; public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { //XPathParser为xml解析器,把mybatis-config.xml流创建了XPathParser实例 this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { //初始化父类BaseBuilder中的全局 Configuration 对象 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false;//标识 this.environment = environment;//此处因为我们只通过配置流实例化XMLConfigBuilder,因此environment=null this.parser = parser;//实例化全局的xml解析器 } }
- 通过mybatis-config.xml配置文件流,创建了 XMLConfigBuilder 对象后,XMLConfigBuilder 其父类维护了全局的Configuration对象,其自身的维护了一个全局的XPathParser的xml解析器
//通过mybatis-config.xml配置文件流,创建XMLConfigBuilder对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //parser.parse()开始解析mybatis-config.xml配置文件,配置文件信息注册到全局Configuration配置类中 return build(parser.parse());
public class XMLConfigBuilder extends BaseBuilder { //解析mybatis配置文件 public Configuration parse() { if (parsed) { //每个XMLConfigBuilder只能解析一次mybatis-config.xml throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //XPathParser是xml文件的解析器,包括检测xml文件格式,默认解析configuration根节点 //通过解析mybatis-config.xml各个节点信息,注册到全局Configuration配置类中 parseConfiguration(parser.evalNode("/configuration")); //返回全局的Configuration对象 return configuration; } //最后逐一解析mybatis-config.xml配置文件中的各个节点 private void parseConfiguration(XNode root) { try { //解析<properties>节点 propertiesElement(root.evalNode("properties")); //解析<settings>节点 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); //解析<typeAliases>节点 typeAliasesElement(root.evalNode("typeAliases")); //解析<plugins>节点 pluginElement(root.evalNode("plugins")); //解析<objectFactory>节点 objectFactoryElement(root.evalNode("objectFactory")); //解析<reflectorFactory>节点 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //解析<reflectorFactory>节点 reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 //解析<environments>节点 environmentsElement(root.evalNode("environments")); //解析<databaseIdProvider>节点 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //解析<typeHandlers>节点 typeHandlerElement(root.evalNode("typeHandlers")); //解析<mappers>节点 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } }
- 回顾我们的Demo工程中,目前的mybatis-config.xml配置文件内容
- 当前 XMLConfigBuilder 对象的 parse() 方法中解析的是我们的 environments 节点和 mappers 节点内容,对应的解析方法分别为:environmentsElement(root.evalNode(“environments”)) 和 mapperElement(root.evalNode(“mappers”))
- 我们在代码注释中提到解析mybatis-config.xml配置文件的各个节点信息后,会将节点信息注册到父类BaseBuilder维护的全局Configuration对象中
- 接下来我们就通过environments节点来看看是XMLConfigBuilder是怎么把节点信息注册到全局Configuration对象的;
- 分析 environments 节点的信息注册到全局Configuration对象
//context此时为节点<environments>信息 private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { //获取<environments>节点属性default的值,既environment=mysql,具体可参照上面配置文件截图 environment = context.getStringAttribute("default"); } //遍历<environments>节点的所有子节点<environment> //根据配置文件,子节点目前只有一个<environment id="mysql"> for (XNode child : context.getChildren()) { //获取子节点<environment id="mysql">中的属性id值为mysql String id = child.getStringAttribute("id"); //判断子节点<environment>属性id的值跟父节点<environments>属性default是否一致,一致则当前的子节点<environment>信息作为mybatis的数据库环境 if (isSpecifiedEnvironment(id)) { //获取<environment>的其子节点<transactionManager type=“JDBC”>,实例化事务工厂对象 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //获取<environment>的其子节点<dataSource type=“POOLED”>,实例化数据源工程对象 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //通过数据源工厂,获取数据源DataSource对象 DataSource dataSource = dsFactory.getDataSource(); //mybatis通过id=mysql实例化mysql的环境,把事务工厂和数据源赋予environmentBuilder构建者创建了mysql数据库环境 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); //最后把mysql数据库环境注册到全局Configuration中 configuration.setEnvironment(environmentBuilder.build()); } } } } private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } else if (id == null) { throw new BuilderException("Environment requires an id attribute."); } else if (environment.equals(id)) { return true; } return false; }
- 在整个environments节点的源码中,我们知道了,XMLConfigBuilder 对象只解析了environments节点中跟属性default值和子节点environment的属性值id一致的节点信息,构建该子节点environment的数据库环境,最后注册到了全局Configuration配置类中
- 通过parseConfiguration方法,我们知道了mybatis配置文件有11个节点,分别是:properties、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、reflectorFactory(旧版本没有该节点)、environments、databaseIdProvider、typeHandlers、mappers
- 每个节点最后解析后信息均注册到了全局Configuration中,具体代码可以在各个节点解析的方法中去查看,而其他节点的解析源码,我们在后面还是一样围绕着demo工程mybatisCode来一一分析。
- 最后本篇中整个源码的分析总结,通过以图的方式帮助整理mybatis-config.xml配置文件初始化的整个过程
下篇开始Mybatis源码分析(3)—配置节点properties源码解析,我们将对mybatis-config.xml配置文件中的各个节点,逐篇的分析,每个节点mybatis源码解析的过程,按照节点解析的顺序,我们优先解析节点properties
相关文章推荐
- Mybatis工作机制源码分析—初始化—mapper配置文件解析
- Mybatis架构设计及源码分析-Mybatis配置文件初始化全过程
- 【mybatis源码分析】原理分析之三:初始化(配置文件读取和解析)
- Mybatis配置文件(mybatis-config.xml )源码分析
- MyBatis源码分析:如何解析配置文件
- mybatis源码学习之执行过程分析(0)——配置文件加载(io包)
- Mybatis3源码分析(三):解析mapper的xml配置文件
- mybatis底层源码分析之--配置文件读取和解析
- Mybatis3源码分析(三):解析mapper的xml配置文件
- Mybatis源码分析(一)- Configuration配置文件详解
- mybatis源码学习之执行过程分析(2)——config.xml配置文件和mapper.xml映射文件解析过程
- Mybatis源码解析之初始化配置文件封装为Configuration源码详解
- MyBatis原理分析之三:初始化(配置文件读取和解析)
- Mybatis 源码分析--Configuration.xml配置文件加载到内存
- velocity源码分析:初始化之配置文件
- MyBatis 源码分析 - 配置文件解析过程
- Heritrix源码分析(二) 配置文件order.xml介绍
- 源码分析-Mybatis初始化过程
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 二
- Hibernate3源码分析之hibernate.cfg.xml配置文件与SessionFactory类