Mybatis源码(一)—SqlSessionFactoryBuilder(获得配置文件)

在使用mybaits时,首先会创建一个SqlSessionFactory对象,该对象是由SqlSessionFactoryBuilder对象,调用该对象的build方法加载全局XML配置的流文件构建出一个SqlSessionFactory对象。

//读取conf.xml
        Reader reader = Resources.getResourceAsReader("conf.xml");
        //创建会话工厂
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
复制代码

SqlSessionFactoryBuilder的源码

public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
复制代码

SqlSessionFactoryBuilder只有一堆重载的build方法,除了build(Configuration)方法,其他方法的参数都是输入流,最终由build(Configuration)方法生成SqlSessionFactory对象,在其中会生成一个XMLConfigBuilder对象,下面来看如何构建Configuration对象。

XMLConfigBuilder解析配置文件

XMLConfigBuilder类名就可以看出,这是用来解析XML配置文件的类,其父类为BaseBuilder。

public class XMLConfigBuilder extends BaseBuilder{
...
}
复制代码

XMLConfigBuilder父类BaseBuilder

其中BaseBuilder还包含了许多子类,这些子类都是用来解析MyBatis各个配置文件,他们通过BaseBuilder父类共同维护一个全局的Configuration对象, XMLConfigBuilder的作用就是解析全局配置文件,调用BaseBuilder其他子类解析其他配置文件,生成最终的Configuration对象。 值得注意的是,这里BaseBuilder构造方法参数是一个初始化的Configuration对象,Configuration对象初始化的时候,内置的别名注册器TypeAliasRegistry注册了默认的别名

Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)
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);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

复制代码

XPathParser类解析config.xml文件

public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }
复制代码

上述代码可以发现在SqlSessionFactoryBuilder.build方法中,new XMLConfigBuilder类时 environment和props值为null,所以,我们主要看new XPathParser方法

字段

private Document document ; // Document 对象
private boolean validation; //是否开启验证
private EntityResolver entityResolver ; // 用于加载本地DTD 文件
pruvate Properties variables ; // mybatis -config.xml 中< propteries > 标签定义的键位对集合
private XPath xpath ; // XPath 对象
复制代码
  • Document(Document对象) Document 对象代表整个 XML 文档,是一棵文档树的根,可为我们提供对文档数据的最初(或最顶层)的访问入口。
  • EntityResolver (加载本地的DTD文件) 如果解析mybatis-config.xml 配置文件,默认联网加载http://mybatis.org/dtd/mybatis-3- config.dtd 这个DTD 文档,当网络比较慢时会导致验证过程缓慢。在实践中往往会提前设置EntityResolver 接口对象加载本地的DTD 文件,从而避免联网加载DTD文件。
  • XPath (XPath对象) XPath 是一种为查询XML 文档而设计的语言,它可以与DOM 解析方式配合使用,实现对XML 文档的解析。
  • validation(是否开启验证标记) 该标记表示设置解析器在解析文档的时候是否校验文档,在创建DocumentBuilderFactory实例对象时进行设置。
  • variables(配置参数集合)。 对应配置文件中节点下定义的键值对集合,包括通过url或者resource读取的键值对集合。

构造方法

XPathParser类提供了一系列的构造函数,所有的构造函数内部都通过通用的commonConstructor()方法实现对相关字段属性的初始化,部分构造函需要通过createDocument()方法把指定的数据源转换成Document对象。 大致可以分为四类

  • 以Document作为参数
  • 以InputStream作为参数
  • 以Reader作为参数
  • 以String作为参数
    Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(reader));
  }
复制代码

createDocument方法

该方法主要实现根据输入源创建Document对象。创建Document对象的过程如下

  • 创建DocumentBuilderFactory对象,并设置相关参数。 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  • 创建DocumentBuilder对象,并设置相关参数。 DocumentBuilder builder = factory.newDocumentBuilder();
  • 解析Document对象。 builder.parse(inputSource);

具体代码如下:

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
    	//创建DocumentBuilderFactory实例对象
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      //设置是否启用DTD验证
      factory.setValidating(validation);

      //设置是否支持XML名称空间
      factory.setNamespaceAware(false);

      //设置解析器是否忽略注释
      factory.setIgnoringComments(true);

      /**
       * 设置必须删除元素内容中的空格(有时也可以称作“可忽略空格”,请参阅 XML Rec 2.10)。
       * 注意,只有在空格直接包含在元素内容中,并且该元素内容是只有一个元素的内容模式时,
       * 才能删除空格(请参阅 XML Rec 3.2.1)。由于依赖于内容模式,因此此设置要求解析器处于验证模式。默认情况下,其值设置为 false。
       */
      factory.setIgnoringElementContentWhitespace(false);

      /**
       * 指定由此代码生成的解析器将把 CDATA 节点转换为 Text 节点,并将其附加到相邻(如果有)的 Text 节点。默认情况下,其值设置为 false。
       */
      factory.setCoalescing(false);

      /**
       * 指定由此代码生成的解析器将扩展实体引用节点。默认情况下,此值设置为 true。
       */
      factory.setExpandEntityReferences(true);

      //创建DocumentBuilder实例对象
      DocumentBuilder builder = factory.newDocumentBuilder();
      //指定使用 EntityResolver 解析要解析的 XML 文档中存在的实体。将其设置为 null 将会导致底层实现使用其自身的默认实现和行为。
      builder.setEntityResolver(entityResolver);

      //指定解析器要使用的 ErrorHandler。将其设置为 null 将会导致底层实现使用其自身的默认实现和行为。
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

复制代码

commonConstructor方法

构造器通用代码块,用于初始化validation、entityResolver、variables、xpath等属性字段。其中,validation、entityResolver、variables三个参数通过参数传递过来;xpath属性是通过XPathFactory创建。具体代码片段:

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }
复制代码

evalXXX方法

XPathParser类提供了一系列的evalXXX方法,见下图,这些方法主要用于解析boolean 、short、long 、int 、String 、Node等类型的信息。底层是通过evaluate()方法实现。其中,evalString()方法中,会通过调用PropertyParser.parse()处理占位符;evalNode()、evalNodes()方法中,根据解析结果会创建XNode对象。具体创建过程,在XNode类源码分析中学习。

Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)

XNode类

XNode类对应了配置文件中一个元素节点的信息。

//org.w3c.dorn.Node对象
  private final Node node;

  //Node节点名称
  private final String name;

  //节点的内容
  private final String body;

  //节点属性集合
  private final Properties attributes;

  //配置文件中<properties>节点下定义的键位对
  private final Properties variables;

  //XPathParser对象,当前XNode对象由此XPathParser对象生成
  private final XPathParser xpathParser;

复制代码

XMLConfigBuilder.parse()方法

此时我们已经得到了XMLConfigBuilder对象,再看SqlSessionFactoryBuilder的build方法,将XMLConfigBuilder实例对象parser调用parser()方法得到的Configuration实例对象config作为参数,调用SqlSessionFactory接口的实现类DefaultSqlSessionFactory构造出SqlSessionFactory对象。 XMLConfigBuilder对象在调用parser()方法时,会读出所有所有配置文件,将配置文件解析后保存在Configuration对象中。

Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)

再查看build方法,参数为Configuration,可以猜测处parse方法是返回一个Configuration对象

Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)

parse方法代码:

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //参数是<configuraton>标签根节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
复制代码

可以发现这里通过parseConfiguration方法使用之前创建的Xparser类,把XML全局配置文件中每一个节点的信息都读取出来,保存在一个Configuration对象中,Configuration分别对以下内容做出了初始化:

private void parseConfiguration(XNode root) {
    try {
      //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"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
复制代码
  • properties 属性
  • settings 设置
  • typeAliases 类型别名
  • typeHandlers 类型处理器
  • objectFactory 对象工厂
  • plugins 插件
  • environments 环境
  • databaseIdProvider 数据库厂商标识
  • mappers 映射器

到了这里,已经可以开始加载配置文件了

总的来说,大概就是这个样子

Mybatis源码(一)---SqlSessionFactoryBuilder(获得配置文件)


很遗憾的说,推酷将在这个月底关闭。人生海海,几度秋凉,感谢那些有你的时光。

原文 

https://juejin.im/post/5f193cea6fb9a07eaa40d26a

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Mybatis源码(一)—SqlSessionFactoryBuilder(获得配置文件)

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址