Mybatis源码分析(二):SqlSessionFactory与框架启动加载

概述

  • Mybatis作为一个SQL管理和执行框架,在web应用程序当中充当数据库访问中间件的角色,即应用程序在接收到请求时,通过DAO层方法从mybatis获取一个数据库连接,然后通过该连接将对应的SQL发送给数据库执行,最后获取返回结果。同时可以通过该连接发送多条SQL,即发送多个请求给数据库,所以该数据库连接的作用就相当于web浏览器中的一个会话。

  • 基于这个业务背景,在mybatis的设计中使用SqlSession来代表该会话,其中在内部封装了一个数据库连接。同时抽象了一个SqlSessionFactory,即sqlSession工厂类,由SqlSessionFactory统一加载mybatis框架的整体配置和mapper.xml的SQL配置,包括提供数据库连接的数据源的引用,mapper.xml的SQL与Java的对应接口和方法的映射关系。

  • 加载完成之后,就可以在应用程序当中直接通过SqlSessionFactory获取一个SqlSession,然后指定需要执行的mapper接口,调用对应的方法,由SqlSession通过SqlSessionFactory引用获取加载好的mapper.xml和mapper接口的映射关系,从而获取该方法对应的需要执行的SQL,最后通过sqlSession自身绑定的数据库连接来执行这条SQL。

  • 基于以上设计,在不使用spring的时候,应用程序的使用示例如下:

    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    SqlSession session = sqlSessionFactory.openSession();
    try {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      Blog blog = mapper.selectBlog(101);
    } finally {
      session.close();
    }
    
  • 详细例子可以参考:JDBC的PreparedStatement和不使用Spring的Mybatis使用示例

SqlSessionFactory的初始化

  • 由以上分析可知,mybatis框架在使用方面,第一步需要完成的是SqlSessionFactory对象的创建:具体为读取配置文件mybatisConfig.xml并加载为SqlSessionFactory对象的一个配置性质属性Configuration来维护,对应mapper.xml则生成内部是MapperProxy代理对象。

  • mybatis提供了一个SqlSessionFactoryBuidler,基于构造者模式来完成SqlSessionFactory对象的创建。核心源码实现如下:核心逻辑为通过builder包的xml子包的XMLConfigBuidler解析mybatisConfig.xml对象,生成对应的Configuration对象,然后将该Configuration对象作为参数创建SqlSessionFactory对象。

    // 解析mybatisConfig.xml配置文件创建SqlSessionFactory,
    // 其中再mybatisConfig.xml内部存在mapper节点来指定mapper.xml文件的配置位置
    public class SqlSessionFactoryBuilder {
    
      // 将mybatisConfig.xml对应的InputStream作为参数
      public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
      }
      
      ...
    
      // 使用XMLConfigBuilder来解析mybatisConfig.xml文件
      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          
          // 调用parse方法完成xml文件的解析并创建对应的Configuration对象
          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.
          }
        }
      }
        
      // 创建SqlSessionFactory对象实例,
      // 使用的是接口的默认实现类DefaultSqlSessionFactory
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
      
     }
    

XML文件解析器XMLConfigBuilder

  • 由以上分析可知,配置文件mybatisConfig.xml主要通过XMLConfigBuilder来解析,具体为parse方法,然后生成对应的Configuration对象,XMLConfigBuilder的parse方法实现如下:

    public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
    }
    
    private void parseConfiguration(XNode root) {
    try {
      
      // 以下方法的参数都是对应mybatisConfig.xml文件中的一个节点
      
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(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"));
      
      // 解析mybatisConfig.xml的mappers标签
      mapperElement(root.evalNode("mappers"));
      
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
    }
    
  • 在内部主要是通过parseConfiguration方法来解析mybatisConfig.xml的各个节点,然后填充到Configuration对象中。

  • 其中对应mapper.xml文件的解析主要是通过mapperElement方法来实现,主要为根据mapper.xml的namespace对应的类全限定名映射到对应的mapper接口,使用内部的buiding包的MapperProxy代理对象来表示,该代理对象集合为对应Configuration对象的mapperRegistry。其次是解析内部的SQL配置,使用mapping包的MappedStatement对象来表示,该代理对象集合则对应Configuration对象的mappedStatements。具体在后面的文章详细分析mapper.xml的解析。

Configuration配置加载

  • 每个SqlSessionFactory关联一个mybatisConfig.xm配置文件,在SqlSessionFactory类对象内部是通过属性Configuration对象来保存这些配置信息。

    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      private final Configuration configuration;
    
      // 只有这个构造函数,使用Configuration存放mybatisConfig.xml中的配置信息
      public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
      }
      
      ...
      
    }
    
  • SqlSessionFactoryBuilder在创建SqlSessionFactory对象时,调用XMLConfigBuilder的parse方法来解析mybatisConfig.xml生成Configuration对象,然后将该Configuration对象作为参数创建SqlSessionFactory对象。

  • mybatisConfig.xml文件的每个节点都对应Configuration对象的一个属性,针对以上所分析的mapper.xml的namespace对应的mapper接口的映射MapperProxy,SQL语句对应的MappedStatement,具体如下:

    public class Configuration {
      // 主要存放数据源引用,如通过数据源获取数据库连接
      protected Environment environment;
    
      ...
    
      // 一级Cache默认为SESSION级别
      protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
      
      ...
      
      // 维护mapper接口对应的代理对象和mapper.xml文件映射,以及对象方法和SQL之间的映射
      protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
      ...
    
      // mapper接口方法的全限定名作为key,如com.xieyz.demo.mapper.UserDAO.getUser,value为SQL语句包装对象
      protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
    
      ...
      
    }
    

总结

基于以上分析可知,mybatis框架的启动加载是基于SqlSessionFactoryBuilder的build方法调用:通过XMLConfigBuidler解析配置文件mybatisConfig.xml,创建并填充配置数据到Configuration对象,然后将该Configuration对象作为参数,创建SqlSessionFactory对象,即在SqlSessionFactory对象内部维护该Configuration对象引用,由于SqlSessionFactory创建的每个SqlSession对象内部都维护了SqlSessionFactory对象的引用,故SqlSession对象可以通过SqlSessionFactory对象间接引用Configuration对象,从而获取配置信息,如mapper接口对应的代理对象mapperProxy,mapper接口方法对应的SQL代理对象mappedStatement。

你可能感兴趣的:(Mybatis)