类加载器ClassLoader:
- 启动类加载器: Bootstrap ClassLoader Load classes from JRE/lib/rt.jar 不是用Java代码编写, 程序中获取不到, 因此无法通过ClassLoader获取这个目录下的Resource File
- 扩展类加载器: Extension ClassLoader Load classes from JRE/lib/ext or Djava.ext.dirs Java代码中可以获取的最父级ClassLoader
- 系统类加载器: AppClassLoader Load classes from classPath or Djava.class.path
- 用户类加载器: User-Defined ClassLoader
TIP: 查找类或者查找文件时, 是从一个类加载器A开始找Parent ClassLoader, 一直找到Extension ClassLoader,
然后执行getBootstrapResource(String)方法从BootstrapClassPath加载Resource File, 如果加载到了直接返回,
否则从Extension ClassLoader回溯至A ClassLoader(双亲委派):
-
调用getLookupCache(String)方法找出之前查找过的Loader(ClassPath)的下标
-
调用getNextLoader(int[], int)方法找出下一个需要查找Resource File的Loader(ClassPath),
2.1 如果缓存的Loader(ClassPath)的下标数组为null, 则调用getLoader(int)方法从当前ClassLoader的Stack urls中获取一个URL — 栈.pop(), 这个栈保存的是Loader(ClassPath)的URL, 再通过URL获取其Loader(ClassPath)并返回 — 查找过的就放进缓存: ArrayList
loaders 2.2 如果缓存的Loader(ClassPath)的下标数组不为null, 则通过下标从ArrayList
loaders中取出Loader(ClassPath)返回 -
获取到Loader(ClassPath)后, 调用URLClassPath.Loader.findResource(String, boolean)方法查找该Loader(ClassPath)下的Resource File的URL
3.1 以该Loader(ClassPath)的ClassPath为基路径, 用户输入的要查找的路径为子路径, 构造一个URL, 如果findResource(String, boolean)的第二个参数为true, 那么将会进行路径合法性的检查: URLClassPath.check(URL);
3.2 对URL指向HTTP Resource的, setRequestMethod(“HEAD”), 并检查ResponseCode, 如果大于等于400, 说明找不到HTTP Resource, 直接返回null
3.3 对URL指向其他Resource的, 设置缓存标志为false
3.4 返回Resource File的URL
-
URL.openStream();
TIP: ClassPath是相对于ClassLoader来说的, 因此每个ClassLoader都有自己的ClassPath, 我们通常说的ClassPath是指AppClassLoader的ClassPath
获取ClassLoader:
-
BootstrapClassLoader: null
String.class.getClassLoader();
ClassLoader.getSystemClassLoader().getParent().getParent(); -
ExtClassLoader:
Bootstrap.class.getClassLoader();
ClassLoader.getSystemClassLoader().getParent(); -
AppClassLoader:
ClassLoader.getSystemClassLoader();
JavaSE场景下: Thread.currentThread().getContextClassLoader();
JavaSE场景下: this.getClass().getClassLoader(); -
ParallelWebappClassLoader:
JavaEE场景下: Thread.currentThread().getContextClassLoader();
JavaEE场景下: this.getClass().getClassLoader(); -
CustomClassLoader:
…
ClassPath路径(打包后):
- 常见工程: classes/
- Web工程: /WEB-INF/classes/
- 普通Java工程: ApplicationName目录下(ApplicationName/)
读取文件流例子:
-
从AppClassLoader开始找((JRE/lib/ext or java.ext.dirs) + (classPath or Djava.class.path)):
ClassLoader.getSystemResourceAsStream();
JavaSE场景下: Thread.currentThread().getContextClassLoader().getResourceAsStream();
JavaSE场景下: this.getClass().getClassLoader().getResourceAsStream(); -
从AppClassLoader开始找(封装后的):
this.getClass().getResourceAsStream(); : 不以/开头时,在此类所在的包下获取资源, 以/开头时, 在ClassPath路径下获取资源 -
从ParallelWebappClassLoader开始找:
JavaEE场景下: Thread.currentThread().getContextClassLoader().getResourceAsStream();
JavaEE场景下: this.getClass().getClassLoader().getResourceAsStream(); -
从WebApp根目录下开始找:
在Servlet中: this.getServletContext().getResourceAsStream(); -
MyBatis的工具: Resources.getResourceAsStream();
-
无法获取查找路径之外的资源: Method threw ‘java.lang.IllegalArgumentException’ exception.
The resource path […] has been normalized to [null] which is not valid -
ResourceBundle.getBundle(String baseName, Locale locale);
TIP:
- ServletContext: Servlet应用上下文, 可以研究一下
- ClassLoader.getBootstrapClassPath(); 获取Bootstrap ClassLoader所加载的Resource File的路径
- 找不到路径时, 就用ClassLoader.getResource("")方法调试,或用ClassLoader.getResourceAsStream(“meta-INF”);或this.getClass().getResource("/");
- System.getProperty(“user.dir”); 和上述问题无关, 是JVM启动时指定的属性



