Mybatis应用和源码学习-把InputStream资源代表的XML文件转换成Document

通过Mybatis应用和源码学习-如何通过指定配置文件获取InputStream 这一章获取的InputStream对象传入到SqlSessionFactoryBuilder.build()方法中把InputStream对象代表的XML文件转化成Document对象,代码入口如下:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder 类解析

首先看下这个类的所有行为,可以看到这个类的主要行为就是通过build方法返回一个SqlSessionFactory对象,只是不同的方法重载而已,除了可以传入字节和字符流外,还可以传入一个Configuration对象来获取SqlSessionFactory对象。
在这里插入图片描述
因为我们的例子中传入的是InputStream参数,所以这里看下通过传入InputStream的Build方法,这个方法又只是执行了自己的一个重载方法,build(inputStream, null, null),

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

当上面方法执行的时候就进入到自己的重载方法build(inputStream, environment, properties),这个方法主要有两个逻辑处理,一个是利用XMLConfigBuilder这个类去把InputStream对象解析成Document对象封装到XMLConfigBuilder对象中,还有一个就是通过解析出来的XMLConfigBuilder对象解析Document对象中对应的XML节点,方法的后面两个参数environment,properties都是通过代码传入的我们没有传,所以这里都是null,代码如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //主要通过XMLConfigBuilder类去解析Config配置文件xml
      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.
      }
    }
  }

这里先看下 这行代码是如何把InputStream对象解析成Document对象的,至于节点的解析处理会在下一篇 Mybatis应用和源码学习-Document节点解析

XMLConfigBuilder类解析

在这里插入图片描述
XMLConfigBuilder类主要作用就是Mybatis用来解析XML文件的其中有四个属性,environment 指定使用哪个环境,parsed 是否已经解析过了,parser XML解析对象,localReflectorFactory 反射工厂。

  /**
   * 用来标记这个配置类是否已经解析过了
   */
  private boolean parsed;
  /**
   *  XPath解析器类
   */
  private final XPathParser parser;
  /**
   * environment 属性
   */
  private String environment;
  /**
   * 反射工厂,
   */
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

然后还有很多的辅助解析Document节点的方法,这一篇主要先解析一下两个构造函数,其余的在后续篇章中解析,下面代码是两个构造函数的代码:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    /**
     * 先获得XMLMapperEntityResolver() config和Mapper xml对象的解析器
     *  在传入XpathParser解析器去解析xml文件
     */
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

第一个构造函数的主要是利用XPathParser对象把InputStream流代表的XML对象解析成Document对象,然后把Document对象封装到自己的属性中,再执行自己的第二个重载的构造函数来给相关的属性赋值,其中主要的逻辑会去新建一个Configuration对象先初始化这个对象的默认构造函数中的初始化操作,然后调用自己的父类BaseBuilder的构造方法,把Configuration,typeAliasRegistry,typeHandlerRegistry三个属性初始化。

Configuration 类

在执行new 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);

    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 类解析XML

在这里插入图片描述
这个类主要是用来解析XML文件的,然后组装成Document对象,有几个属性的含义如下,把XML文件转成Document对象的docuement属性,一个是否需要校验的标识 validation ,一个实体转换处理类 entityResolver,一个属性对象 variables,最后一个解析XML的xpath对象
在这里插入图片描述
在执行new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()) 这个方法的时候会传入一个实体转换解析类XMLMapperEntityResolver,然后进入下面这个方法执行相关后续操作:

public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    //这个方法主要是对几个属性赋值
    commonConstructor(validation, variables, entityResolver);
    //把inputStream对象封装成java中的inputSource对象
    this.document = createDocument(new InputSource(inputStream));
  }

上述的commonConstructor(validation, variables, entityResolver);方法没有做上面重要的工作,只是初始化了一些属性,其中一个属性xpath对象是利用JAVA API实例化的。

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    //这个工厂方法最后返回的是new XPathFactoryImpl()对象工厂实现
    XPathFactory factory = XPathFactory.newInstance();
    //根据上面的工厂返回一个xpath对象
    this.xpath = factory.newXPath();
  }

主要来看下createDocument(new InputSource(inputStream));这个方法,因为XML的处理过程在这个方法中执行,这里主要是按照JAVA API解析XML的要求组装对象,然后调用JAVA DocuementBuilder类提供的API解析XML,返回一个Document对象

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      //这里主要是按照JAVA API解析XML的要求组装对象,然后调用JAVA DocuementBuilder类提供的API解析XML,返回一个Document对象
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      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 {
          // NOP
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }
BaseBuilder 类解析

在这里插入图片描述
这个类主要作用就是通过默认构造函数来处理以下这几个属性。

  /**
   * Configuration类,主要映射mybatis配置xml里面所有的内容
   */
  protected final Configuration configuration;
  /**
   *  别名解析注册类
   */
  protected final TypeAliasRegistry typeAliasRegistry;
  /**
   * 类型处理注册类
   */
  protected final TypeHandlerRegistry typeHandlerRegistry;

  /**
   *  在构造函数里面给三个变量赋值
   * @param configuration
   */
  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
最后看下这整个的处理流程时序图

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值