mybatis_3源码阅读日记_容器的加载与初始化

SqlSessionFactory的创建

SqlSessionFactory是通过SqlSessionFactoryBuilder工厂类创建的,而不是直接使用构造器。
SqlSessionFactoryBuilder的主要代码如下:

 //SqlSessionFactoryBuilder是一个建造者模式
 public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  
 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);
  }

SqlSessionFactory提供了根据字节流、字符流以及直接使用org.apache.ibatis.session.Configuration配置类(后续我们会详细讲到)三种途径的读取配置信息方式,但究其根本都是首先将XML配置文件构建为Configuration配置类,然后将Configuration设置到SqlSessionFactory默认实现DefaultSqlSessionFactory的configurationz字段并返回。主要解析配置文件的逻辑都委托给XMLConfigBuilder了。
XMLConfigBuilder的主要代码

public class XMLConfigBuilder extends BaseBuilder {
	....
	  /**
	   * Description 构造XMLConfigBuilder
	   * @param reader 流
	   * @param environment 
	   * @param props
	   * @author songhongwei s19744
	  * @time 202/1/5 16:44
	   **/
	  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
	    //XPathParser用于SAX解析xml,解析器硬编码为XMLMapperEntityResolver
	    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
	  }
	  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	    //创建Configuration对象,用来保存配置文件的配置
	    super(new Configuration());
	    ErrorContext.instance().resource("SQL Mapper Configuration");
	    this.configuration.setVariables(props);
	    this.parsed = false;
	    this.environment = environment;
	    this.parser = parser;
	  }
	....
}

标签解析类

public class XPathParser {
 private final Document document;
 private boolean validation;
 //XML标签解析器,可使用不通的解析器,通过构造方法传进来,使用到了策略模式
 private EntityResolver entityResolver;
 private Properties variables;
 private XPath xpath;
    /**
    * Description 构造方法
    *
    * @param reader
    *            Reader
    * @param validation
    *            是否进行DTD 校验
    * @param variables
    *            属性配置
    * @param entityResolver 
    *            XML实体节点解析器
    * @author songhongwei s19744
    * @time 202/1/5 19:54
    **/
 public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
   commonConstructor(validation, variables, entityResolver);
   this.document = createDocument(new InputSource(reader));
 }
 
 //创建document
 private Document createDocument(InputSource inputSource) {
   // important: this must only be called AFTER common constructor
   try {
     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);
   }
 }
}

XMLConfigBuilder以及解析Mapper文件的XMLMapperBuilder都继承于BaseBuilder。他们对于XML文件本身技术上的加载和解析都委托给了XPathParser,最终用的是jdk自带的xml解析器而非第三方比如dom4j,底层使用了xpath方式进行节点解析。new XPathParser(reader, true, props, new XMLMapperEntityResolver())的参数含义分别是Reader,是否进行DTD 校验,属性配置,XML实体节点解析器。

entityResolver,跟Spring的XML标签解析器一样,有默认的解析器,也有自定义的,主要使用了策略模式,在这里mybatis硬编码为XMLMapperEntityResolver。
XMLMapperEntityResolver的定义如下:

package org.apache.ibatis.builder.xml;

import org.apache.ibatis.io.Resources;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;

/**
 * Offline entity resolver for the MyBatis DTDs.
 *
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class XMLMapperEntityResolver implements EntityResolver {

  private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
  private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
  private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";

  private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";

    /**
     * Converts a public DTD into a local one. 将公共的DTD转换为本地模式
     * @param publicId The public id that is what comes after "PUBLIC"
     * @param systemId The system id that is what comes after the public id.
     * @return The InputSource for the DTD
     * @throws org.xml.sax.SAXException If anything goes wrong
     */
  @Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
    try {
      if (systemId != null) {
        String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
        if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
          return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
        } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
          return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
        }
      }
      return null;
    } catch (Exception e) {
      throw new SAXException(e.toString());
    }
  }

 private InputSource getInputSource(String path, String publicId, String systemId) {
    InputSource source = null;
    if (path != null) {
      try {
        InputStream in = Resources.getResourceAsStream(path);
        source = new InputSource(in);
        source.setPublicId(publicId);
        source.setSystemId(systemId);
      } catch (IOException e) {
        // ignore, null is ok
      }
    }
    return source;
  }

}

mybatis解析的时候,引用了本地的DTD文件,和本类在同一个package下,其中的ibatis-3-config.dtd应该主要是用于兼容用途。在其中getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId)的调用里面有两个参数publicId(公共标识符)和systemId(系统标示符),他们是XML 1.0规范的一部分,在mybatis中,systemId指定的是mybatis-3-config.dtd和ibatis-3-config.dtd
调用栈

build:64, SqlSessionFactoryBuilder (org.apache.ibatis.session)
-->build:77, SqlSessionFactoryBuilder (org.apache.ibatis.session)
	--><init>:89, XMLConfigBuilder (org.apache.ibatis.builder.xml)
		--><init>:127, XPathParser (org.apache.ibatis.parsing)
			-->createDocument:261, XPathParser (org.apache.ibatis.parsing)
				-->parse:339, DocumentBuilderImpl (com.sun.org.apache.xerces.internal.jaxp)
					-->parse:243, DOMParser (com.sun.org.apache.xerces.internal.parsers)
						-->parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers)
							-->parse:771, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
								-->parse:842, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
									-->scanDocument:505, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
										-->next:602, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
											-->next:959, XMLDocumentScannerImpl$PrologDriver (com.sun.org.apache.xerces.internal.impl)
												-->next:1045, XMLDocumentScannerImpl$DTDDriver (com.sun.org.apache.xerces.internal.impl)
													-->dispatch:1151, XMLDocumentScannerImpl$DTDDriver (com.sun.org.apache.xerces.internal.impl)
														-->resolveEntityAsPerStax:997, XMLEntityManager (com.sun.org.apache.xerces.internal.impl)
															-->resolveEntity:110, EntityResolverWrapper (com.sun.org.apache.xerces.internal.util)
																-->resolveEntity:58, XMLMapperEntityResolver (org.apache.ibatis.builder.xml)

未完待续

上一篇:Mybatis源码学习(四)Mapper的整体流程


下一篇:Spring整合Ibatis之SqlMapClientDaoSupport