首先看一段代码 ,以下这段代是码是从classpath中加载资源
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("classpath:test/a/");
URL url = resource.getURL();
... //根据url读取文件
org.springframework.core.io.DefaultResourceLoader.getResource(String)源码如下:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
代码中红色标记处是处理从classpath中加载资源的,这里创建一个个ClassPathResource实例,第二个参数是设置一个classloader, 再看 org.springframework.core.io.DefaultResourceLoader.getClassLoader方法:
@Override
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
我们没有对this.classLoader赋值,默认为null, 继续看ClassUtils.getDefaultClassLoader()方法:
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
红色标记处,显示使用ClasspathResource使用的ClassLoader为: Thread.currentThread().getContextClassLoader();
当前线程的ContextClassLoader, 这个和当前程序运行在哪种容器(或环境)有关了
比如运行在Tomcat8.5.6容器中,ContextClassLoader为:
org.apache.catalina.loader.ParallelWebappClassLoader
再回过头来看,org.springframework.core.io.ClassPathResource.getURL()方法:
public URL getURL() throws IOException {
URL url = resolveURL();
if (url == null) {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
}
return url;
}
继续看,org.springframework.core.io.ClassPathResource.resolveURL():
protected URL resolveURL() {
if (this.clazz != null) {
return this.clazz.getResource(this.path);
}
else if (this.classLoader != null) {
return this.classLoader.getResource(this.path);
}
else {
return ClassLoader.getSystemResource(this.path);
}
}
方法实现中
如果设置了clazz调用clazz.getResource,否则
如果设置了classLoader调用classLoader.getResource,否则
使用 ClassLoader.getSystemResource
还是以我们运行在Tomcat8.5.6为例,就会调用
org.apache.catalina.loader.ParallelWebappClassLoader.getResource,通过tomcat文档
https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html
看到
Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:
- Bootstrap classes of your JVM
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
- System class loader classes (described above)
- Common class loader classes (described above)
所以资源加载顺序为
1.
WEB-INF\classes
2.classpath
3.
$CATALINA_BASE/lib
说到底spring的ResouceLoader就是根据我们设置的ClassLoader来进行资源加载