mybatis source code - initialization configuration

Posted by gere06 on Wed, 17 Jun 2020 07:42:02 +0200

Our debugging environment:

Debug code:

public class MyaaTest {
    public static void main(String[]args) {
        String resource = "config.xml";
        try {
            //1. Read configuration file
            InputStream in= Resources.getResourceAsStream(resource);
            SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(in); //Resolve profile
            //2. Open connection
            SqlSession session=sessionFactory.openSession();
            String statement="com.it.dao.UserMapper.selectById";
            //3. Method call
            User user=session.selectOne(statement);

            System.out.println(user.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Step 1: enter the Resources class, which is familiar with xml

public static InputStream getResourceAsStream(String resource) throws IOException {
  return getResourceAsStream(null, resource);
}
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); //[in]
  if (in == null) { //If all five classloader s fail, you can only report an error!
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}

Step 2: enter ClassLoaderWrapper, which is obviously the wrapper class of classloader

Look at the members:

public class ClassLoaderWrapper {

  ClassLoader defaultClassLoader;
  ClassLoader systemClassLoader;
  ClassLoaderWrapper() {
      systemClassLoader = ClassLoader.getSystemClassLoader();
  }
...
}

We use the static method. There are two classloaders in this method (when we studied the jvm, we learned classLoader. Its main function is to find the class file in the file system and load it into the jvm). What we want to find here is xml.

Note that classLoader is null

public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
  return getResourceAsStream(resource, getClassLoaders(classLoader)); //Five classloader s are injected
}

Look at the second parameter returned:

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  return new ClassLoader[]{
      classLoader,  //null
      defaultClassLoader, //null
      Thread.currentThread().getContextClassLoader(),
      getClass().getClassLoader(),
      systemClassLoader};
}

Five class loaders are returned. The first one and the second one are Null. The default class loader does not find the instantiation process.

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  for (ClassLoader cl : classLoader) { //Traverse Classloader
    if (null != cl) {
      InputStream returnValue = cl.getResourceAsStream(resource); //Try to load
      if (null == returnValue) { //Find failed, try again
        returnValue = cl.getResourceAsStream("/" + resource);
      }
      if (null != returnValue) { return returnValue; } //Launch after loading successfully, return to stream
    }
  }
  return null;
}

Our configuration file, in the root directory, the third classloader finds our xml file

 

Step 3: enter SqlSessionFactoryBuilder class

The structure of this class is divided into two parts (there is also a column outside)

A group is divided into three builds and a dobuild(). The first group mainly introduces readers, and the second group introduces inputStream. Let's go to the second group naturally

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

These overloads are just the difference of the incoming parameters, but in order to expand, they have to do so (just like the principle that multiple parameters can be returned in go)

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
}

 

Step 4: enter XMLConfigBuilder class

public class XMLConfigBuilder extends BaseBuilder {
  private boolean parsed; //Resolved, XPath parser, environment
  private XPathParser parser;
  private String environment;
}

The [constructor] structure of this class is the same as SqlSessionFactoryBuilder, which is generally divided into two groups of readers + streams, and finally flows into the builder method for execution

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
  this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

It uses stream to build the XPathParser class, which is passed into the next final constructor

XPath parser: the package of jdk is used. It's more convenient to use

public class XPathParser {

  private Document document;
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;
}

The final constructor, which configures all the properties,

//The above six encapsulate the parser and flow into here
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  super(new Configuration());     ///First call the parent class to initialize Configuration
  ErrorContext.instance().resource("SQL Mapper Configuration"); //The error context is set to SQL Mapper Configuration(XML file configuration), so that an error is reported later
  //Set all Properties to Configuration
  this.configuration.setVariables(props);
  this.parsed = false;
  this.environment = environment;
  this.parser = parser;
}

Ready to start parsing: parse()

Give a template xml to be parsed

<?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 <environments default="development">
 <environment id="development">
  <transactionManager type="JDBC"/>
  <dataSource type="POOLED">
   <property name="driver" value="${driver}"/>
   <property name="url" value="${url}"/>
   <property name="username" value="${username}"/>
   <property name="password" value="${password}"/>
  </dataSource>
 </environment>
 </environments>
  <mappers>
     <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>
public Configuration parse() {
//If it has been parsed, an error will be reported
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
//Resolution configuration
private void parseConfiguration(XNode root) {
  try {
    //Step by step analysis
    //issue #117 read properties first
    //1.properties
    propertiesElement(root.evalNode("properties"));
    //2. Type alias
    typeAliasesElement(root.evalNode("typeAliases"));
    //3. Plug in
    pluginElement(root.evalNode("plugins"));
    //4. Target factory
    objectFactoryElement(root.evalNode("objectFactory"));
    //5. Object packaging factory
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //6. Setting
    settingsElement(root.evalNode("settings"));
    // read it after objectFactory and objectWrapperFactory issue #631
    //7. Environment
    environmentsElement(root.evalNode("environments"));
    //8.databaseIdProvider
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //9. Type processor
    typeHandlerElement(root.evalNode("typeHandlers"));
    //10. Mapper
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

View the parent class of this class:

public abstract class BaseBuilder {
  //Three things need to be configured: type alias registration and type processor registration
  protected final Configuration configuration; //Save the result of parsing 
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;
}

Return the configuration result.

 

The result is encapsulated and easy to use.

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

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

There are many contents about parse(), we will talk about them in the next section

Topics: xml Mybatis SQL Session