Mybatis3 source code reading (I)

Time:2021-9-2

Introduction to mybatis and learning objectives of the framework source code

  
  Mybatis is one of the most popular Java persistence layer frameworks. It eliminates most JDBC duplicate code, parameter setting and result set mapping through XML configuration. Although mybatis is one of the most popular persistence layer frameworks, its comments are relatively few compared with other open source frameworks such as spring / netty. In order to better learn and understand the design ideas behind mybatis, as a senior developer, it is necessary to deeply study and understand the source code of the excellent framework in order to better learn from its ideas. At the same time, as the main application scenario of design patterns, the framework can better understand the essence of design patterns by studying the source code of excellent frameworks. The learning framework source code is different from the learning framework itself. We should not only be familiar with each feature provided by the framework in a detailed and complete way, but also understand the initialization process of the framework itself. In addition, more importantly, we can’t quickly browse which function is implemented and encapsulated through which class or interface. For the implementation of core features and initialization process, We should achieve the following objectives:
  

  1. For the core features and internal functions, how to implement them and what data structure to adopt, while studying, think about whether it is reasonable or unreasonable. In particular, the call frequency of many features and functions is very low, or the number of elements contained in many sets is 1 when more than 99%, In this case, many design decisions do not need to be specially selected or suitable for complex implementations with large amount of data or high concurrency. For many internal data structures and auxiliary methods, we need to know not only their functions themselves, but also their role in the context.
  2. For the core features and internal functions, which design patterns are adopted for the specific implementation, and the rationality of using this design pattern;
  3. Most frameworks are designed to be extensible, and mybatis is no exception. It provides many extension points, such as the most commonly used plug-ins, language drivers, actuators, object factories, object wrapper factories and so on. Therefore, we should know how to expand according to requirements to meet our own needs if necessary.

Example overall diagram

Mybatis3 source code reading (I)

2. Start with an instance

@Slf4j
public class HelloWorld {




    public static void main(String[] args) {


        String resource = "config.xml”;// configuration file
        Reader reader;
        try {
            //2. Read the configuration file
            reader = Resources.getResourceAsReader(resource);//2. Read the configuration file
            //3. Build sqlsessionfactory
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
            //4. Create a sqlsession
            SqlSession sqlSession = sqlMapper.openSession();
            try {
                //5. Execute a query
                User user = sqlSession.selectOne("com.blueheart.mybatis.mapper.UserMapper.getUser", 1);
                log.error("user id={},name={}",user.getId(),user.getName());
            }finally {
                //6. End closing
                sqlSession.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }








    }




}

From the above code process, let’s see that this process involves several objects and related methods

0.config.xml
1.Resources.getResourceAsReader(resource);
2.SqlSessionFactory
3.SqlSessionFactoryBuilder
4.SqlSession

The configuration file is as follows:

<?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="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

Analyze this line of code and return a reader (character stream) from a resource

//2. Read the configuration file
            reader = Resources.getResourceAsReader(resource);//2. Read the configuration file

Resources.class

This class is a resource reading class, which specifically loads the XML file to generate a reader. Here I have a question. Why do most frameworks need to get a resource class by themselves? Is it easy to load a file or URL resource? Is that true? Let’s take a look at the source code of resource.class:

/**
 * A class to simplify access to resources through the classloader.
 *A simple class that accesses resources through the class loader
 *
 * @author Clinton Begin
 */
public class Resources {


  //Class loader wrapper class? Why? We'll see later
  private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();


  /**
   * Charset to use when calling getResourceAsReader.
   * null means use the system default.
   *The character set uses the system by default. It is well considered. Generally, we don't consider it or write it directly
   */
  private static Charset charset;


 
  public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
    //Delegate the returned process to classloaderwrapper
    URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
    if (url == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return url;
  }


 


 
  public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    //Delegate the returned process to classloaderwrapper
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
  }


  /**
   * Returns a resource on the classpath as a Properties object
   *Get the properties object of a resource. The resource is a. Properties file
   * @param resource The resource to find
   * @return The resource
   * @throws java.io.IOException If the resource cannot be found or read
   */
  public static Properties getResourceAsProperties(String resource) throws IOException {
    Properties props = new Properties();
    try (InputStream in = getResourceAsStream(resource)) {
      props.load(in);
    }
    return props;
  }


 
  public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException {
    Properties props = new Properties();
    try (InputStream in = getResourceAsStream(loader, resource)) {
      props.load(in);
    }
    return props;
  }



  public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
  }


 
  public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(getResourceAsStream(loader, resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
    }
    return reader;
  }


  public static File getResourceAsFile(String resource) throws IOException {
    return new File(getResourceURL(resource).getFile());
  }

  public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException {
    return new File(getResourceURL(loader, resource).getFile());
  }

There are only a few most important methods in this resource class, that is, the actual execution methods. There is another class classloaderwrapper involved. It seems that all these actual loads are
Let’s take a look at how classloaderwrapper actually loads resources

/**
 * A class to wrap access to multiple class loaders making them work as one
 *A wrapper accesses multiple class loaders to make it look like it is accessing a resource
 *
 * @author Clinton Begin
 */
public class ClassLoaderWrapper {


  ClassLoader defaultClassLoader;
  ClassLoader systemClassLoader;


  ClassLoaderWrapper() {
    try {
      systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {
      // AccessControlException on Google App Engine
    }
  }
   …
   //One of the core methods, the other is the same
   InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  for (ClassLoader cl : classLoader) {
    if (null != cl) {


      // try to find the resource as passed
      InputStream returnValue = cl.getResourceAsStream(resource);


      // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
      if (null == returnValue) {
        returnValue = cl.getResourceAsStream("/" + resource);
      }


      if (null != returnValue) {
        return returnValue;
      }
    }
  }
  return null;
}

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

}

What does this class do? Is to find a suitable class loader in order to load the corresponding resources. The core method is the last classloader [] getclassloaders (classloader classloader), which returns the list of class loaders in its own order. Generally, both classloader and defaultclassloader are null, so thread. Currentthread(). Getcontextclassloader() is the most used class loader, This involves a basic knowledge point, JVM class loading process model.

summary

  1. The resource loading of the framework is more complex than the general use, so there needs to be a mechanism to load correctly. The first thing to solve is to load from different classloaders to ensure that the resources can be found accurately
  2. You can return different streams, such as byte streams, character streams, properties, or URLs
  3. The conversion process should be considered as comprehensive as possible, and the file has coding, so the specified coding format can be set

Additional knowledge

  • Java has three classloaders by default, from top to bottom:
    *
  • Bootstarp ClassLoader
  • Ext ClassLoader
  • System ClassLoader
    *
  • “Parental entrustment”
  • The so-called “parent delegation” means that when a class is loaded, it will be delegated to the parent class loader to load it first. When the parent class loader cannot load, it will try to load it by itself. Therefore, the loading of the whole class is “top-down”. If it is not loaded, a classnotfoundexception exception will be thrown.
    *
  • As mentioned above, bootstrap classloader is the top class loader. In fact, ext classloader and system classloader were loaded from the beginning.
    *
  • Classloader, called extension class loader, is responsible for loading Java’s extension class library. Java is loaded by default_ All jars under home / JRE / lib / ext / (including the jar package you manually put in).
    *
  • System classloader is called system class loader, which is responsible for loading all jars and class files in the application classpath directory, including the jar package we normally run under the specified CP parameter.

Class loader Foundation