如何通过指定配置文件获取InputStream
继上一篇Mybatis应用和源码学习-怎么使用Mybatis代替jdbc操作数据库有说到通过Mybatis代替JDBC来操作数据库,其中有一步是通过给定的配置文件路径来获取这个路径对应的InputStream流的逻辑操作具体代码如下:
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
从上面的代码可以看出Mybatis把资源的获取委托给了一个工具类Resources去处理了,下面我们就先来看看这个Resources工具类有哪些属性和行为:
Resources工具类的解析
从Resources类的属性与行为截图可以看出,该类只有两个常量,一个charset 字符编码,一个个classLoaderWrapper 类加载器,主要就是通过这个对象去加载资源的,其余的都是行为,其中一个默认的构造器这个没什么逻辑处理,还有一个根据指定的字符串获取一个类的行为classForName,其余的都是根据字符或者字节流去获取的方法和方法重载。
Resources.getResourceAsStream(resource)方法解析
/**
* Returns a resource on the classpath as a Stream object
* 根据classpath路劲下的路径,返回classpath路径下的InputStream对象
*
* @param resource The resource to find 根据这个路径去找InputStream流
* @return The resource 返回InputStream流
* @throws java.io.IOException If the resource cannot be found or read 如果这个InputStream对象没有找到就抛出io异常
*/
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}
该方法被调用后方法体没有执行特殊操作,只是执行了下面自己的一个重载方法,把主要逻辑都委托给重载方法去执行了。
/**
* Returns a resource on the classpath as a Stream object
*根据classpath路劲下的路径,返回classpath路径下的InputStream对象
*
* @param loader The classloader used to fetch the resource 这个classloader 用来寻找InputStream对象
* @param resource The resource to find 根据这个路径去找InputStream流
* @return The resource 返回InputStream流
* @throws java.io.IOException If the resource cannot be found or read 如果这个InputStream对象没有找到就抛出io异常
*/
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);
}
return in;
}
这里主要处理逻辑就是通过Resources类的classLoaderWrapper的getResourceAsStream方法去获取资源,如果获取到了InputStream资源就返回该对象,如果没有获取到该对象就抛出一个IO异常,并给出信息,没有找到给定的路径的资源对象,
下面看下classLoaderWrapper的getResourceAsStream方法处理了啥
/**
* Get a resource from the classpath, starting with a specific class loader
* 根据指定的classloader 获得在classpath路径下的 InputStream 对象
* @param resource - the resource to find 根据这个路径去找InputStream流
* @param classLoader - the first class loader to try
* @return the stream or null
*/
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
这个方法跟Resources类的逻辑差不多都是执行自己的重载方法,不过这个方法的参数会先去获取一下用来加载资源的类加载器,然后再执行自己的重载方法,
先看下getClassLoader()这个获取类加载器的方法逻辑:
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
这个方法其实就是返回了一个ClassLoader数组,但是其中5个对象,通常情况都是使用了Thread.currentThread().getContextClassLoader()这个获取当前线程的上下文的类加载器,上述方法也是使用的这个
下面接着看下getResourceAsStream 的重载方法逻辑
/**
* Try to get a resource from a group of classloaders
*
* @param resource - the resource to get
* @param classLoader - the classloaders to examine
* @return the resource or null
*/
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) {
if (null != cl) {
// try to find the resource as passed
//尽力 使用java的ClassLoader类的资源获取方法 去查找资源对象
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;
}
该方法的逻辑就是通过获取的classLoader类加载器来操作数据,如果累加器都为空就直接返回null,因为没有可用的类加载器,如果不为空,就调用JAVA的类ClassLoader对应的API获取资源,这个获取资源的动作就委托给JAVA的API去处理了,资源这个JAVA API是怎么获取的这里不去深究处理过程,比较繁琐,咱们只要会使用这个API去调用就行了。如果通过API获取到了InputStream资源就返回这个资源对象,如果没有获取到会再该资源路径的前面加一个“/”字符再执行一次API,尝试再次获取该资源,如果获取到就返回该对象,获取不到就返回NULL.
至此,Mybatis通过Resources的getResourceAsStream方法根据给定的资源路径获取该路径对应的InputStream对象的代码逻辑就结束了。