关于mybatis:Mybatis21-从读取流到创建SqlSession发生了什么

37次阅读

共计 7209 个字符,预计需要花费 19 分钟才能阅读完成。

[TOC]

咱们应用 sqlSession 之前,须要去获取配置文件,获取 InputStream 输出流,通过 SqlSessionFactoryBuilder 获取 sqlSessionFactory 对象,从而获取sqlSession

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

1.Resources.getResourceAsStream(“mybatis.xml”)到底做了什么?

1. 首先咱们来看 InputStream is = Resources.getResourceAsStream("mybatis.xml"); 这句话到底替咱们干了什么,上面能够看出在外面调用了另一个外部办法,resource 是全局配置的文件名:

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

2. 跟进办法中,咱们能够晓得在外面调用 ClassLoaderWrapper 类的一个实例对象的 getResourceAsStream() 办法,这个 classLoaderWrapper 怎么来的呢?这个是 Resources.class 的一个成员属性, 那么这个 ClassLoaderWrapper 是什么货色呢?

Resources.class 中咱们只是应用 private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper(); 创立一个 classLoaderWrapper 对象。
ClassLoaderWrapper其实是一个 ClassLoader(类加载器) 的包装类,其中蕴含了几个 ClassLoader 对象,一个 defaultClassLoader,一个systemClassLoader,通过外部管制,能够确保返回正确的类加载器给零碎应用。咱们能够当成一个mybatis 自定义过的类加载器。

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. 咱们能够看出调用了上面这个外部办法, 外面调用了封装的办法,一个是获取以后的类加载器,另一个是传进来的文件名:

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

4. 查看 getClassLoaders() 这个办法,能够看到外面初始化了一个类加载器的数组,外面有很多个类加载器,包含默认的类加载器,以后线程的上下文类加载器,零碎类加载器等。

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

5. 进入 getResourceAsStream(String resource, ClassLoader[] classLoader) 这个办法外部, 咱们能够看到外面遍历所有的类加载器,应用类加载器来加载获取 InputStream 对象,咱们能够晓得外面是抉择第一个适宜的类加载器,如果咱们不传类加载器进去,那么第一个本人定义的类加载器就是 null,那么就会默认抉择第二个默认类加载器,而且咱们能够晓得如果文件名后面没有加“/”,获取到空对象的话,会主动加上“/”再拜访一遍:

 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. 咱们进入类加载器加载资源文件的代码中, 咱们能够看到首先获取全门路的url,而后再调用openStream()

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

6.1. 咱们跟进 getResource(name) 这个办法,咱们能够看到外面都是调用 parentgetResource()办法,如果曾经是父加载器,那么就应用 getBootstrapResource(name) 获取,如果获取进去是空的,再依据 getBootstrapResource(name) 办法获取。

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 咱们跟进去getBootstrapResource(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 咱们看到 getBootstrapClassPath() 这个办法,这个办法的外面调用了引入的包,读取的是类加载器的加载门路, 这个办法到此为止,再深刻就回不去了:)。

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

6.1.1.2 咱们看 ucp.getResource(name) 这个办法,咱们能够看到在外面调用了这个办法, 这个办法次要是查找缓存,而后遍历找到第一个符合条件的加载器来获取resource,到此咱们不再深究上来,得往上一层回头看:

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;
  }

咱们晓得 getBootstrapResource(name) 外面次要是 url(文件资源的门路),而后应用 url.openStream() 去获取 stream 流:

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

咱们来看 openConnection()办法, 外面调用的是一个形象办法,获取的是一个 URLConnection(url 连贯对象):

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

再看 getInputStream()这个办法,咱们能够看到这是一个接口办法,咱们找到 FileURLConnection 的这个办法, 这是一个单线程解决文件 URL 的 inputstream 的办法:

  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;
  }

到这里,整个获取 inputstream 的过程曾经完结,只有把返回值往上一层返回就能够失去这个配置文件所须要的 inputstream。

2. new SqlSessionFactoryBuilder().build(is)的运行原理

首先 SqlSessionFactoryBuilder 的无参数构造方法是没有任何操作的:

public SqlSessionFactoryBuilder() {}

那么咱们看 build(is) 这个办法, 外面调用了一个封装办法,一个是 inputstream,一个是 string,一个是属性对象:

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

跟进去, 咱们能够看到在外面应用了 xmlconfigbuilder,也就是 xml 配置结构器,实例化一个 xml 配置对象,可想而知,也就是咱们的 mybatis.xml 所对应的配置对象结构器,在外面调用了另一个 build()办法:

  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;
  }

咱们能够看到调用的另一个 build 办法, 也就是应用配置对象构建一个 DefaultSqlSessionFactory 对象,在下面返回这个对象,也就是咱们的 sqlsessionFactory。

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

3. sqlSessionFactory.openSession()获取 sqlSession

咱们能够看到其实这个是 sqlSessionFactory 的一个接口,其实现类是DefaultSqlSessionFactory,那么办法如下:

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

咱们查看 openSessionFromDataSource() 这个办法,从名字能够大略晓得是从数据源加载 Sqlsession,外面能够指定执行器类型,事物隔离级别,还有是否主动提交,如果不设定,那么默认是 null 以及 false,在办法内次要做的是将配置文件对象的环境取出来结构事务工厂,配置执行器等,返回一个 DefaultSqlSession 的实例。

  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;
  }

到此为止,一个 sqlsession 对象就依据配置文件创立进去了。

此文章仅代表本人(本菜鸟)学习积攒记录,或者学习笔记,如有侵权,请分割作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有谬误之处,还望指出,感激不尽~

技术之路不在一时,山高水长,纵使迟缓,驰而不息。
公众号:秦怀杂货店

正文完
 0