MyBatis核心流程三大阶段之初始化阶段

目录

1.初始化阶段整体流程

2.加载配置文件

3.解析配置文件、将配置文件中的信息装载到Configuration中

4.根据Configuration创建SqlSessionFactory并返回


1.初始化阶段整体流程

      前面几篇分析了MyBatis的日志、数据源和缓存模块的源码,本篇将分析MyBatis核心流程三大阶段的第一阶段:初始化阶段。MyBatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中,Configuration为单例,生命周期为应用级。MyBatis初始化流程大致有三步:

       1)加载配置文件

       2)解析配置文件、将配置文件中的信息装载到Configuration中。

       3)根据Configuration创建SqlSessionFactory并返回。

2.加载配置文件

下面我们来看一段经典查询操作:

String resouce = "config/mybatis/mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resouce);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sqlSessionFactory.openSession();
user = session.selectOne("com.luoxn28.dao.UserDao.getById", 1);

       以上代码经过了MyBatis初始化、创建sqlSession、执行sql语句3个过程。首先由mybatis-config.xml配置文件创建SqlSessionFactory,然后由session工厂创建SqlSession对象,执行SQL语句。当然初始化的第一阶段:扫描配置文件所在包路径并加载

3.解析配置文件、将配置文件中的信息装载到Configuration中

       让我们来看一下梦开始的地方:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

       跟进build()方法,我们可以看到new了一个XMLConfigBuilder对象并调用了parse()方法:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //创建XMLConfigBuilder对象解析XML配置
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //将XML配置解析成Configuration对象,通过Configuration对象创建SqlSessionFactory
      return build(parser.parse());
    } 
    ....
}

       跟进parse()方法,我们可以看到parser.evalNode("/configuration"),evalNode为xml结点解析器,可以解析指定参数结点的信息,再看这个"/configuration"有点眼熟丫,这不就是mybatis.xml的根节点嘛:

public Configuration parse() {
    ...
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

       看到这,我们就不得不提初始化的三大金刚了,分别是XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder。

       XMLConfigBuilder:主要负责解析mybatis-config.xml

       XMLMapperBuilder:主要负责解析映射配置文件

       XMLStatementBuilder:主要负责解析映射配置文件中的sql节点

三大金刚图解:

       MyBatis中的xml文件是由三大金刚读取到Configuration类中,那么我们来看下Configuration类的数据结构:

       Configuration类的源码实在太多,童鞋们先对这个类有个大致印象,了解下该类中有哪些成员变量对应存储着些什么数据。下面主要列举几个比较重要的成员变量:

       MapperRegistry:mapper接口动态代理工厂类的注册中心。通过mapperProxy实现InvocationHandler接口,其中的MapperProxyFactory用于生成动态代理的实例对象;

       ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素; 

       MappedStatement:用于存储mapper.xml文件中select、insert、update和delete节点,同时还包含了这些节点的重要属性;

       SqlSource:mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

接上面XMLConfigBuilder开始解析"/configuration"节点:

parseConfiguration(parser.evalNode("/configuration"));
private void parseConfiguration(XNode root) {
    try {
      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);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

       点进去一看,就是对照着MyBatis官网主配置文件中的元素一个一个的进行解析

       在解析"mappers"节点的时候,就引入了XMLMapperBuilder开始对映射配置文件进行解析

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      ...
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

       对应官网提供的节点信息进行解析

       下面大家猜也猜到了,在解析具体select、insert、update、delete的时候,引入了XMLStatementBuilder对节点数据进行解析:

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

       继三大金刚分别解析自己负责的xml文件之后,Configuration对象的数据被填充完毕,初始化的第二阶段:解析配置文件,将数据装载进Configuration对象完成。

4.根据Configuration创建SqlSessionFactory并返回

       第三阶段就是根据SqlSessionFactoryBuilder的内部方法直接返回一个DefaultSqlSessionFactory:

public class SqlSessionFactoryBuilder {
   ...
   public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
   }
}

       此工厂内封装了Configuration对象:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  private final Configuration configuration;
  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }
  ...
}

       初始化阶段图解:

       至此,MyBatis初始化阶段完成。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值