Build the XmlConfigBuilder to prepare the base environment for parsing XML files

As mentioned earlier, the XmlConfigBuilder object is used to parse the global Configuration file of Mybatis to get an instance of the Configuration object.

XmlConfigBuilder exposes six constructors that can be grouped into two categories based on the input stream type of the Mybatis configuration file:

Handles byte stream configuration files and character stream configuration files respectively.

// Handle the mybatis configuration for byte stream type
XMLConfigBuilder(InputStream inputStream);
XMLConfigBuilder(InputStream inputStream, String environment);
XMLConfigBuilder(InputStream inputStream, String environment, Properties props);

// Handle the mybatis configuration for the character stream type
XMLConfigBuilder(Reader reader);
XMLConfigBuilder(Reader reader, String environment);
XMLConfigBuilder(Reader reader, String environment, Properties props);
Copy the code

In the actual code implementation, the calls to these two constructors will end up on the following two methods, depending on the file stream (the first method is triggered because we passed in the byte stream earlier):

/ * * *@paramInputStream configuration file inputStream *@paramEnvironment Indicates the current environment ID *@paramProps User-defined attribute */
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(
                    inputStream,/*XML file input stream */
                    true./* Enable authentication */
                    props, Property / * and * /
                    new XMLMapperEntityResolver() /*XML entity parser */
            ) /* Create a new instance of the XML parser */
            , environment/* Environment object */
            , props / * * / Property parameters
    );
}
Copy the code
/ * * *@paramReader configuration file input stream *@paramEnvironment Indicates the current environment ID *@paramProps User-defined attribute */
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader,
                    true/* Whether to validate DTD */
                    , props / * * /
                    , new XMLMapperEntityResolver() /* DTD file finder for XML */
            )/* Build an XPath parser */
            , environment /* Current environment */
            , props / * * /
    );
}
Copy the code

In both of these methods, mybati creates an XPathParser object from the input stream of the configuration file, regardless of whether the stream is byte stream or character stream. It then calls the private constructor of XMLConfigBuilder XMLConfigBuilder(XPathParser Parser parser, String Environment, Properties props) to complete the construction of the XmlConfigBuilder object.

/ * * *@paramParser XPath parser *@paramEnvironment Indicates the environment ID *@paramProps User-defined attribute */
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    // Initializes the Configuration object while registering partial aliases and language drivers
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    // Set variables
    this.configuration.setVariables(props);
    // Initialize the parse tag
    this.parsed = false;
    // Initialize the environment container
    this.environment = environment;
    // Initializes the Xml address parser
    this.parser = parser;
}
Copy the code

XPathParser is a parser class internally defined by Mybatis that reads data in XML documents based on XPath language.

XPath, or XML Path Language, is a Language for determining the location of parts of AN XML document.

Mybatis passed in a new XMLMapperEntityResolver() object when it created the XPathParser object. We’ll talk about this later, but we’ll ignore it and the implementation of the XPathParser object for now. Continue with the XmlConfigBuilder construction process.

new XPathParser(reader,
                true/* Whether to validate DTD */
                , props / * * /
                , new XMLMapperEntityResolver() /* DTD file finder for XML */
        )
Copy the code

In the private constructor of XmlConfigBuilder, Mybatis calls the no-argument constructor of Configuration to generate a Configuration object, And pass the Configuration object into the constructor of the parent class of XmlConfigBuilder BaseBuilder to complete the initialization of BaseBuilder.

// Initializes the Configuration object while registering partial aliases and language drivers
super(new Configuration());
Copy the code

The no-argument constructor of Configuration contains some basic data preparation, including registering common type aliases, registering scripting language drivers, and configuring default scripting language drivers.

  • Type aliases:Click to learn about type aliases

    A type alias is a short name for a Java type. It is only relevant for XML configuration and exists only to reduce the redundancy of fully qualified names of classes.

  • Scripting Language Driver: Click on the scripting language driver in the section entitled “Pluggable native languages in dynamic SQL.

Can look at the simple example test: org. Apache. Ibatis. Submitted. Language. LanguageTest# testLangVelocityWithMapper

public Configuration(a) {
    // Register an alias

    // Register the JDBC alias
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    // Register transaction management aliases
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    // Register the JNDI alias
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    // Register the pooled data source alias
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    // Register as a pooled data source
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    // Register permanent cache
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    // Register a first-in, first-out cache
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    // Register the least recently used cache
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    // Register soft cache
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    // Register weak cache
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    // Register the provider that handles the database ID
    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    // Register xmL-based language drivers
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    // Register static language drivers (usually not needed)
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    // Register Sl4j logs
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    // Register the Commons log
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    // Register log4j logs
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    // Register log4j2 logs
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    // Register JDK log
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    // Register standard output logs
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    // There is no log for registration
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    / / register additional
    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    / / register JAVASSIST
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    // XML language driver is used by default
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    // Support native language drivers
    languageRegistry.register(RawLanguageDriver.class);
}
Copy the code

!!!!! Note that mybatis defines the default scripting language driver here as XMLLanguageDriver alias XML.

We said earlier that the XMLConfigBuilder object is a subclass of BaseBuilder, and in BaseBuilder there are three final modified properties:

/** * Mybatis configuration information */
protected final Configuration configuration;
/** * Type alias registry */
protected final TypeAliasRegistry typeAliasRegistry;
/** * Type conversion processor registry */
protected final TypeHandlerRegistry typeHandlerRegistry;
Copy the code

Their initialization assignment takes place in the BaseBuilder constructor:

public BaseBuilder(Configuration configuration) {
      // Keep the reference
      this.configuration = configuration;
      // Synchronize the type alias registry from Mybatis configuration
      this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
      // Synchronize the type handler registry from Mybatis configuration
      this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
Copy the code

In the constructor above, the BaseBuilder caches references to the Configuration object, Both its own typeHandlerRegistry and TypeAliasRegistry properties point to the typeHandlerRegistry and TypeAliasRegistry properties of the Configuration object.

public class Configuration {.../** * Type processor registry */
   protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
   /** * A registry of type aliases, mainly used to execute input and exit arguments in SQL statements and as shorthand for some classes */
   protected final TypeAliasRegistry typeAliasRegistry = newTypeAliasRegistry(); . }Copy the code
  • Type Processors: Click to learn about myBatis type processors

When MyBatis sets a parameter in a PreparedStatement or fetches a value from a result set, it uses a type handler to convert the obtained value to a Java type in an appropriate manner.

After calling the BaseBuilder constructor, XMLConfigBuilder passes a user-defined collection of variables to the Configuration object, which is saved for later Configuration parsing.

// Cache user-defined variables
this.configuration.setVariables(props);
Copy the code

The parse() method is then allowed to be called by resetting the parse flag and changing its state to not being parsed:

// Initialize the parse tag
this.parsed = false;
Copy the code

Record the current environment information for later configuration parsing.

// Initialize the environment container
this.environment = environment;
Copy the code

Then, save the Xml address resolver currently in use:

// Initializes the Xml address parser
 this.parser = parser;
Copy the code

At this point, the construction of the XMLConfigBuilder object is complete. In addition to a few properties involved in the constructor, XMLConfigBuilder also has a constant property localReflectorFactory, which is hardcoded in the code as DefaultReflectorFactory. We’ll see what it does later, but leave it at that:

/** * to create {@linkOrg. Apache. Ibatis. Reflection. * / Reflector} object factory
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
Copy the code

Now let’s go back to the XPathParser object that XMLConfigBuilder depends on.

XPathParser is constructed in many ways, and in addition to requiring the user to pass in the corresponding XML document, it can also accept some optional parameters.

Here, the incoming XML Document can take many forms, from an XML address link, an input stream, or even a Document object.

There are three optional parameters to pass:

  • The first is validation of Boolean type, which indicates whether to validate XML documents against a DTD. The default value is false.

  • The second parameter, variables of type Properties, represents user-defined property objects that can be read and used as placeholders throughout the configuration file.

  • The third argument is an EntityResolver object of type EntityResolver, which is used to find a DTD validation file for the caller.

    The EntityResolver parameter, mentioned earlier, is hardcoded as an object of type XMLMapperEntityResolver in the XmlConfigBuilder constructor.

    new XPathParser(reader,
                    true/* Whether to validate DTD */
                    , props / * * /
                    , new XMLMapperEntityResolver() /* DTD file finder for XML */
            )/* Build an XPath parser */
    Copy the code

    His code is relatively simple and defines six constants:

    /** * systemId for IBATIS global configuration file */
    private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
    /** * IBATIS Mapper configuration file systemId */
    private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
    /** * MYBATIS systemId */
    private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
    /** * MYBATIS Mapper systemId */
    private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
    /** * MYBATIS global configuration file corresponding to DTD file */
    private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
    /** * MYBATIS MAPPER configuration file corresponding to the DTD file */
    private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
    
    Copy the code

    We also implement the EntityResolver’s resolveEntity method, in which XMLMapperEntityResolver matches a corresponding DTD validation file for the passed systemId:

    /** * Get the DTD file from the local file **@paramPublicId Public identifier *@paramSystemId System identifier *@return DTD
      */
     @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)) {
                     // Mybatis global config file 'config. XML' corresponds to the DTD file
                     return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
                 } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
                     // Mybatis data mapping configuration file '* mapper.xml' corresponding DTD file
                     returngetInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); }}return null;
         } catch (Exception e) {
             throw newSAXException(e.toString()); }}Copy the code

    Click for systemId and publicId

    • SystemId: System identifier, usually used to directly indicate the actual location of the corresponding DTD resource, often application-wide.
    • PublicId: Public identifier. It is a globally unique identifier, usually associated with a specific DTD, that indirectly indicates the location of a DTD file, and is often used across multiple applications.

    EntityResolver and its implementation class XMLMapperEntityResolver is a standard policy schema implementation.

    The policy pattern is a common behavioral design pattern that allows us to define a series of algorithms and encapsulate them so that they are interchangeable. The policy pattern allows algorithms to change independently of the clients that use them.

Because when we create the sqlSessionFactory object, we pass in the byte stream of the configuration file, So the actual constructor for calling XPathParser is XPathParser(InputStream InputStream, Boolean Validation, Properties variables, EntityResolver EntityResolver)(the remaining constructors are similar to this method code) :

/**
 * XPathParser
 *
 * @paramInputStream XML file inputStream *@paramValidation whether DTD validation * is performed@paramVariables user-defined property objects that can be read and used as placeholders throughout the configuration file@paramEntityResolver custom parsers that find DTD validation files */
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    // Call the generic constructor
    commonConstructor(validation, variables, entityResolver);
    // Convert the user's incoming XML file stream into a Document object
    this.document = createDocument(new InputSource(inputStream));
}
Copy the code

The code in this constructor can be divided into two parts, commonConstructor, a generic method for initializing attributes, and createDocument, a method for parsing and retrieving the Document object corresponding to the XML file.

The commonConstructor method is relatively simple, with the only notable exception being an object instance that hardcodes xpath attributes and assigns values of type xpath as follows:

  /** * Public construction argument, which is used to configure the DocumentBuilderFactory **@paramValidation whether to validate DTD *@paramVariables *@paramEntityResolver entityResolver */
    private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
        // Whether to validate the DTD
        this.validation = validation;
        // Configure the XML entity parser
        this.entityResolver = entityResolver;
        // Attribute configuration
        this.variables = variables;

        // Initialize XPath
        XPathFactory factory = XPathFactory.newInstance();
        this.xpath = factory.newXPath();
    }
Copy the code

The createDocument method parses the XML file to get the Document object based on the current configuration:

    /** * Creates a text object ** based on the input source@paramInputSource inputSource *@returnThe text object */
    private Document createDocument(InputSource inputSource) {
        // important: this must only be called AFTER common constructor
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // Whether to validate parsed documents
            factory.setValidating(validation);
            // Whether XML namespace support is provided
            factory.setNamespaceAware(false);
            // Ignore comments
            factory.setIgnoringComments(true);
            // Ignore whitespace
            factory.setIgnoringElementContentWhitespace(false);
            // Whether to parse the CDATA node into a TEXT node
            factory.setCoalescing(false);
            // Whether to expand the entity reference node
            factory.setExpandEntityReferences(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            // Configure the parser for XML documents, now mainly XMLMapperEntityResolver
            builder.setEntityResolver(entityResolver);
            // The handler is incorrectly configured
            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 {}});// Parse out the DOM tree
            return builder.parse(inputSource);
        } catch (Exception e) {
            throw new BuilderException("Error creating document instance. Cause: "+ e, e); }}Copy the code

After executing these two methods, the construction of XPathParser is complete, and XPathParser holds both the Document object and an Xpath accessor, Many of the next operations on XPathParser use Xpath to read the contents of the Document object.

Here are the attributes of the XPathParser object:

public class XPathParser {
    /** * XML object */
    private final Document document;
    /** * Whether to validate DTD */
    private boolean validation;
    /** * DTD entity parser */
    private EntityResolver entityResolver;
    /** * User-defined property objects that can be read and used as placeholders throughout the configuration file. * /
    private Properties variables;
    /** * XML address accessor */
    private XPath xpath;
  }
Copy the code

At this point, the construction of the XMLConfigBuilder object and its dependencies is almost complete, and it’s time to parse the MyBatis configuration file.

Follow me and learn more together