一.前言
本人是一个java后端开发新手,由于近期的项目中添加了mybatis技术,虽然技术会使用,但是不太了解配置文件是如何加载读取到的,所以按照自己的理解整理出一份说明。
<如有不足之处,麻烦指出>
二.底层代码解析
图上时序图步骤解析说明<附上代码块,序号和图中序号对应>:
1.一般代码中加载配置只需要一行代码搞定,如下
//代码中从类路径中加载核心配置文件resource = "mybatis-comfig.xml "
InputStream inputStream = Resources.getResourceAsStream(resource);
2.重载的方法中创建一个类加载器ClassLoader实例对象loader,并调用类加载器包装类classLoaderWrapper的方法
getResourceAsStream(resource, loader)
/*
* 以流对象的形式返回类路径上的资源
*
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource); //调用Resources内部重载方法,参数为路径名
}
/*
* 以流对象的形式返回类路径上的资源
*
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); //返回IO流对象InputStream
if (in == null) throw new IOException("Could not find resource " + resource);
return in;
}
3 && 4.classLoaderWrapper来加载器包装类的getResourceAsStream(resource, loader)方法
/*
* Get a resource from the classpath, starting with a specific class loader
*
* @param resource - the resource to find
* @param classLoader - the first class loader to try
* @return the stream or null
*/
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
//getClassLoaders(classLoader)为获取所有类型的类加载器
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
//4.自调getClassLoaders(classLoader)方法,返回一个类加载器数组,内部包含5类加载器
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader, //由参数指定的默认类加载器
defaultClassLoader, //系统指定的类加载器
Thread.currentThread().getContextClassLoader(), //当前线程绑定的类加载器
getClass().getClassLoader(), //加载当前类所使用的类加载器
systemClassLoader}; //系统类加载器
}
5 && 6.循环使用每一个类加载器去加载配置路径资源,每个类最多加载两次,第一次直接以配置文件名作为路径加载,如果没有加载到资源,则拼接一个"/"形成一个新的路径名再次加载
/*
* 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
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;
}
}
//两次都没加载到,返回null,下一个类加载器classLoader继续重复上面操作
return null;
}
//6.每个类加载器classLoader去调用方法getResourceAsStream(resource)方法,内部自调方法getResource(name)
/**
* Returns an input stream for reading the specified resource.
* 返回用于读取指定资源的输入流
* <p> The search order is described in the documentation for {@link
* #getResource(String)}. </p>
*
* @param name
* The resource name
*
* @return An input stream for reading the resource, or <tt>null</tt>
* if the resource could not be found
*
* @since 1.1
*/
public InputStream getResourceAsStream(String name) {
URL url = getResource(name); //自调
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
//内部自调方法getResource(name)
/**
* Finds the resource with the given name. A resource is some data
* (images, audio, text, etc) that can be accessed by class code in a way
* 查找具有给定名称的资源。资源是一些数据(图像、音频、文本等),可以通过类代码以某种方式访问
* that is independent of the location of the code.
*
* <p> The name of a resource is a '<tt>/</tt>'-separated path name that
* identifies the resource.
*
* <p> This method will first search the parent class loader for the
* resource; if the parent is <tt>null</tt> the path of the class loader
* built-in to the virtual machine is searched. That failing, this method
* will invoke {@link #findResource(String)} to find the resource. </p>
*
* @param name
* The resource name
*
* @return A <tt>URL</tt> object for reading the resource, or
* <tt>null</tt> if the resource could not be found or the invoker
* doesn't have adequate privileges to get the resource.
*
* @since 1.1
*/
public URL getResource(String name) {
URL url;
if (parent != null) {
//parent 为此类ClassLoader类加载器中初始化委托的父类装入器
//private final ClassLoader parent;
url = parent.getResource(name);
} else {
url = getBootstrapResource(name); //自调getBootstrapClassPath方法返回一个URLClassPath实例对象
}
if (url == null) {
url = findResource(name);
}
return url;
}
7 && 8 && 9.自调getBootstrapClassPath方法返回一个URLClassPath实例对象
/**
* Find resources from the VM's built-in classloader.
* 从VM的内置类加载器中查找资源。
*/
private static URL getBootstrapResource(String name) {
URLClassPath ucp = getBootstrapClassPath();
//8.调用URLClassPath类中getResource(name)返回Resource实例对象
Resource res = ucp.getResource(name);
return res != null ? res.getURL() : null;
}
//A:返回用于查找系统资源的URLClassPath。
static URLClassPath getBootstrapClassPath() {
return sun.misc.Launcher.getBootstrapClassPath();
}
10.classLoaderWrapper将最终获取的IO流InputStream对象加载的配置资源返还给Resource
/*
* 以流对象的形式返回类路径上的资源
*
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); //返回IO流对象InputStream
if (in == null) throw new IOException("Could not find resource " + resource);
return in;
}
//获取到的inputStream流对象返回Resource<逆向反推到最开始的那段代码>
//代码中从类路径中加载核心配置文件mybatis-comfig.xml
InputStream inputStream = Resources.getResourceAsStream(resource);