mybatis学习_SqlSessionFactory构建过程

本文详细介绍了MyBatis中SqlSessionFactory的构建过程,包括Configuration、SqlSessionFactoryBuilder、XPathParser和XMLConfigBuilder等组件的作用。SqlSessionFactoryBuilder通过XMLConfigBuilder解析XML配置文件,生成Configuration对象,最终构建SqlSessionFactory。解析过程中,对mappers节点的处理涉及到mapperRegistry和MappedStatement的注册。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用mybatis功能前,我们先得创建一个 SqlSessionFactory 对象,创建过程如下。我们实例化了一个 SqlSessionFactoryBuilder对象,然后让其加载mybatis的配置文件,构建出一个SqlSessionFactory 对象。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

1. 相关组件

介绍SqlSessionFactory 对象的构建过程前,我们先熟悉一下参与这个构建过程的相关组件。以便后面我们更好的分析源码

1.1 Configuration

见名知意,这是 mybaits提供的一个配置类。mybatis所有的配置信息,以及某些配置的默认值都封装在此类中。在构建SqlSessionFactory对象时,mybatis会将传入的xml配置文件的配置信息映射到configuration配置类中。并且,configuration还为我们的别名注册器注册了默认的别名。

 public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    ......

1.2 SqlSessionFactory 和 SqlSessionFactoryBuilder

SqlSessionFactory 是一个构建SqlSession的工厂。SqlSession可以被认为是与数据库一次会话。SqlSessionFactory提供了非常多的重载的 openSession 方法,用来构建SqlSession,以及一个获取配置类getConfiguration方法,用来动态的获取配置信息或更改配置信息。其实现类DefaultSqlSessionFactory内就维护了一个final的Configuration对象。我们通过通常所说的SqlSessionFactory 就是DefaultSqlSessionFactory。

同理,SqlSessionFactoryBuilder 就是用来构建SqlSessionFactory的建造者,其中维护了许多重载的build方法,用于构建SqlSessionFactory。最后所有的build方法又指向了同一个重载的 build(Configuration config) 方法,用于最终的创建SqlSessionFactory对象。

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  @Override
  public SqlSession openSession() {......}
  ......
}


public class SqlSessionFactoryBuilder {

    ......

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        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.
      }
    }
  }
    ......

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

1.3 XPathParser 和 XMLConfigBuilder

XPathParser 是一个mybatis内置的xml文件解析器,其中维护了一个Document对象。XPathParser 在实例化时,就会将传入的xml文件信息转换成一个Document对象,并提供了一系列的解析Document对象的方法。

XMLConfigBuilder 就是用来生成我们的配置类的建造者,其中维护一个XPathParser 对象,其父类BaseBuilder维护一个Configuration 对象。XMLConfigBuilder利用XPathParser 解析xml文件中的各个节点,最后将解析到的信息封装在 Configuration 对象中。其核心方法 parse方法,返回一个 Configuration 对象。

public class XMLConfigBuilder extends BaseBuilder {
  private final XPathParser parser;
  ......
  public Configuration parse() {......}
  ......
}

public abstract class BaseBuilder {
  protected final Configuration configuration;
  ......
}

public class XPathParser {
  private final Document document;
  ......
}

 

2. 构建过程

通过对上面相关组件的了解,SqlSessionFactory构建的大致流程也就比较清晰了。

  1. 实例化一个 SqlSessionFactoryBuilder对象,调用其build方法,并将xml信息作为参数传入,获取到SqlSessionFactory。
  2. SqlSessionFactoryBuilder 利用xml信息作为参数创建了一个XmlConfigBuilder对象,并调用其parse方法生成一个Configuration对象。
  3.  XmlConfigBuilder 的 parse 方法构建并返回了 Configuration 对象。
  4. SqlSessionFactoryBuilder拿到 Configuration后,实例化了一个 DefaultSqlSessionFactory返回。

不难发现,SqlSessionFactory构建过程的核心就是 Configuration 对象的构建,而这个过程的具体实现就在XmlConfigBuilder的parse方法中。

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //标记为已解析状态
    parsed = true;
    //这里的parser就是XmlConfigBuilder维护的XPathParser对象
    //这里拿到了xml的configuration节点下的所有内容,并交给parseConfiguration方法处理
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      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);
    }
  }

parseConfiguration方法调用了一系列的方法去解析xml配置文件中的各个节点,这里我们重点看看解析mappers节点的 mapperElement方法。

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      //遍历 mappers节点下的子节点
      for (XNode child : parent.getChildren()) {
        //解析 package 子节点
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //解析<mapper resource="***.xml"/>形式的配置
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } 
          //解析<mapper url="***.xml"/>形式的配置
          else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } 
          //解析<mapper mapperClass ="***"/>形式的配置
          else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

mappers节点的解析可以分成两个部分来看

1. 如果遍历到package子节点,则将该包下所有的Class注册到Configuration的mapperRegistry中。若是mapper子节点的class属性,则将指定的class注册到Configuration的mapperRegistry中。mapperRegistry会根据接口class的全限定名找到对应的XML配置文件进行解析。

2. 如果遍历到mapper子节点的resource或者url属性,就直接是实例化一个 XmlMapperBuilder对象对xml进行解析。

xml映射文件的解析过程这里不详细叙述。XmlMapperBuilder最终会将映射文件解析成一个MappedStatement对象,并将其注册到Configuration对象中。

当所有的节点都解析完成后SqlSessionFactory的构建过程也就基本完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值