Mybatis [2.1]-What happened from reading the stream to creating the SqlSession?

[TOC]
pexels-pixabay-459225

sqlSessionBefore we use it, we need to get the configuration file, get the InputStreaminput stream, and get it by SqlSessionFactoryBuildergetting the sqlSessionFactoryobject sqlSession.

InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();

1. What exactly did Resources.getResourceAsStream("mybatis.xml") do?

1. First, let's look at InputStream is = Resources.getResourceAsStream("mybatis.xml");what this sentence did for us. The following can see that another internal method is called in it. Resource is the file name of the global configuration:

public static InputStream getResourceAsStream(String resource) throws IOException {
    // 从这里字面意思是传一个空的类加载器进去,还有全局配置文件名,从方法名的意思就是
    // 将配置文件读取,转化成输入流
    return getResourceAsStream((ClassLoader)null, resource);
  }

2. In the follow-up method, we can know the method of calling ClassLoaderWrapperan instance object of the class. How did getResourceAsStream()this classLoaderWrappercome about? This is Resources.classa member attribute, so ClassLoaderWrapperwhat is this?

In this Resources.class, we just use to private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();create an classLoaderWrapperobject.
ClassLoaderWrapperIn fact, it is a ClassLoader(class loader) wrapper class, which contains several ClassLoaderobjects, one defaultClassLoader, one systemClassLoader, through internal control, you can ensure that the correct class loader is returned to the system for use. We can use it as a mybatiscustomized class loader.

public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    } else {
      return in;
    }
  }

3. We can see that the following internal method is called, which calls the encapsulated method, one is to get the current class loader, and the other is the file name passed in:

  public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
    return this.getResourceAsStream(resource, this.getClassLoaders(classLoader));
  }

4. Looking at getClassLoaders()this method, you can see that an array of class loaders is initialized, and there are many class loaders, including the default class loader, the context class loader of the current thread, and the system class loader.

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

5. Entering getResourceAsStream(String resource, ClassLoader[] classLoader)this method, we can see that it traverses all the class loader, use the class loader to load the InputStreamobject, we can know that the first suitable class loader is selected, if we don’t pass the class loader Go in, then the first self-defined class loader is null, then the second default class loader will be selected by default, and we can know that if the file name is not prefixed with " /", if an empty object is obtained, it will be automatically added /Visit again on " ":

 InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    ClassLoader[] arr$ = classLoader;
    int len$ = classLoader.length;
    for(int i$ = 0; i$ < len$; ++i$) {
      ClassLoader cl = arr$[i$];
      if (null != cl) {
        InputStream returnValue = cl.getResourceAsStream(resource);
        if (null == returnValue) {
          returnValue = cl.getResourceAsStream("/" + resource);
        }
        if (null != returnValue) {
          return returnValue;
        }
      }
    }
    return null;
  }

6. We enter the code of the class loader to load the resource file, we can see that the full path is first obtained url, and then call openStream():

public InputStream getResourceAsStream(String name) {
    URL url = getResource(name);
    try {
        return url != null ? url.openStream() : null;
    } catch (IOException e) {
        return null;
    }
}

6.1. We follow getResource(name)this method, we can see that there are calling parentthe getResource()method, if a parent is already a loader, then use the getBootstrapResource(name)Get, get out if empty, then according to getBootstrapResource(name)acquisition method.

public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

6.1.1 Let's follow upgetBootstrapResource(name);

private static URL getBootstrapResource(String name) {
        URLClassPath ucp = getBootstrapClassPath();
        Resource res = ucp.getResource(name);
        return res != null ? res.getURL() : null;
    }

6.1.1.1 We see getBootstrapClassPath()this method, which calls the imported package, and reads the load path of the class loader. This method ends here, and we can’t go back any further :).

static URLClassPath getBootstrapClassPath() {
        return sun.misc.Launcher.getBootstrapClassPath();
    }

6.1.1.2 We look at ucp.getResource(name)this method, we can see that this method is called in it, this method is mainly to find the cache, and then traverse to find the first qualified loader to obtain resource, so far we will not go into further details, we have to go Looking back at the previous level:

public Resource getResource(String var1, boolean var2) {
    if (DEBUG) {
      System.err.println("URLClassPath.getResource(\"" + var1 + "\")");
    }

    int[] var4 = this.getLookupCache(var1);

    URLClassPath.Loader var3;
    for(int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {
      Resource var6 = var3.getResource(var1, var2);
      if (var6 != null) {
        return var6;
      }
    }

    return null;
  }

We know that getBootstrapResource(name)it is mainly url (the path of the file resource), and then use it url.openStream()to get the stream:

public final InputStream openStream() throws java.io.IOException {
        return openConnection().getInputStream();
    }

Let's look at the openConnection() method, which calls an abstract method, and obtains a URLConnection (url connection object):

public URLConnection openConnection() throws java.io.IOException {
        return handler.openConnection(this);
    }

Looking at the getInputStream() method again, we can see that this is an interface method. We find this method of FileURLConnection, which is a single-threaded inputstream method for processing file URLs:

  public synchronized InputStream getInputStream() throws IOException {
    this.connect();
    if (this.is == null) {
      if (!this.isDirectory) {
        throw new FileNotFoundException(this.filename);
      }
      FileNameMap var3 = java.net.URLConnection.getFileNameMap();
      StringBuffer var4 = new StringBuffer();
      if (this.files == null) {
        throw new FileNotFoundException(this.filename);
      }
      Collections.sort(this.files, Collator.getInstance());
      for(int var5 = 0; var5 < this.files.size(); ++var5) {
        String var6 = (String)this.files.get(var5);
        var4.append(var6);
        var4.append("\n");
      }
      this.is = new ByteArrayInputStream(var4.toString().getBytes());
    }
    return this.is;
  }

At this point, the entire process of obtaining the inputstream has ended. As long as the return value is returned to the upper layer, the inputstream required by this configuration file can be obtained.

2. The operating principle of new SqlSessionFactoryBuilder().build(is)

First of all, the parameterless construction method of SqlSessionFactoryBuilder has no operation:

public SqlSessionFactoryBuilder() {
  }

Then we look at build(is)this method, which calls an encapsulation method, one is inputstream, one is string, and the other is an attribute object:

public SqlSessionFactory build(InputStream inputStream) {
    return this.build((InputStream)inputStream, (String)null, (Properties)null);
  }

Following up, we can see that xmlconfigbuilder, which is the xml configuration constructor, is used in it to instantiate an xml configuration object. It is conceivable that it is the configuration object constructor corresponding to our mybatis.xml, which is called inside Another build() method:

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      var5 = this.build(parser.parse());
    } catch (Exception var14) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
      ErrorContext.instance().reset();

      try {
        inputStream.close();
      } catch (IOException var13) {
        ;
      }

    }

    return var5;
  }

We can see another build method called, which is to use the configuration object to construct a DefaultSqlSessionFactory object, and return this object on it, which is our sqlsessionFactory.

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

3. sqlSessionFactory.openSession() to get sqlSession

We can see that this is actually sqlSessionFactoryan interface, and its implementation class is DefaultSqlSessionFactory, then the method is as follows:

  public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
  }

We check openSessionFromDataSource()this method, from the name we can roughly know that the Sqlsession is loaded from the data source, which can specify the actuator type, transaction isolation level, and whether to automatically submit. If not set, the default is null and false. The main method is What it does is to take out the environment of the configuration file object to construct the transaction factory, configure the executor, etc., and return an DefaultSqlSessioninstance.

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    DefaultSqlSession var8;
    try {
      Environment environment = this.configuration.getEnvironment();
      TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      Executor executor = this.configuration.newExecutor(tx, execType);
      var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
      this.closeTransaction(tx);
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
      ErrorContext.instance().reset();
    }
    return var8;
  }

So far, an sqlsessionobject is created based on the configuration file.

This article only represents my (the rookie) study accumulation record, or study notes, if there is any infringement, please contact the author to delete it. No one is perfect, so is the article, the writing is immature, not good at the next, do not spray, if there are mistakes, I hope to point out, I am grateful~

The road to technology is not at a time, the mountains are high and the rivers are long, even if it is slow, it will not stop.
Public number: Qinhuai grocery store

Guess you like

Origin blog.51cto.com/13604316/2668461