FileSystemResourceLoader继承自DefaultResourceLoader,并重写了getResourceByPath(String path)方法,返回FileSystemContextResource类型资源,源码如下:

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.io;

/**
 * {@link ResourceLoader} implementation that resolves plain paths as
 * file system resources rather than as class path resources
 * (the latter is {@link DefaultResourceLoader}'s default strategy).
 *
 * <p><b>NOTE:</b> Plain paths will always be interpreted as relative
 * to the current VM working directory, even if they start with a slash.
 * (This is consistent with the semantics in a Servlet container.)
 * <b>Use an explicit "file:" prefix to enforce an absolute file path.</b>
 *
 * <p>{@link org.springframework.context.support.FileSystemXmlApplicationContext}
 * is a full-fledged ApplicationContext implementation that provides
 * the same resource path resolution strategy.
 *
 * @author Juergen Hoeller
 * @since 1.1.3
 * @see DefaultResourceLoader
 * @see org.springframework.context.support.FileSystemXmlApplicationContext
 */
public class FileSystemResourceLoader extends DefaultResourceLoader {

	/**
	 * Resolve resource paths as file system paths.
	 * <p>Note: Even if a given path starts with a slash, it will get
	 * interpreted as relative to the current VM working directory.
	 * @param path the path to the resource
	 * @return the corresponding Resource handle
	 * @see FileSystemResource
	 * @see org.springframework.web.context.support.ServletContextResourceLoader#getResourceByPath
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		if (path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemContextResource(path);
	}


	/**
	 * FileSystemResource that explicitly expresses a context-relative path
	 * through implementing the ContextResource interface.
	 */
	private static class FileSystemContextResource extends FileSystemResource implements ContextResource {

		public FileSystemContextResource(String path) {
			super(path);
		}

		@Override
		public String getPathWithinContext() {
			return getPath();
		}
	}

}

FileSystemResourceLoader表示从文件系统中加载资源,所以咱们用FileSystemResourceLoader加载资源时应该给出资源的绝对路径,假设在classpath下有一个文件叫test.xml,咱们加载它该怎么写呢? 可以这么写:

@Controller
public class LoginController  {				
	@RequestMapping(value="/index.html")
	public String loginPage() throws IOException {		
		ResourceLoader resourceLoader=new FileSystemResourceLoader();
		Resource resource=resourceLoader.getResource("D:\\ALANWANG-AIA\\Horse-workspace\\chapter3\\target\\classes\\test.xml");
		System.out.println(resource.getInputStream()); 
		return "index";
	}	  
}
启动服务器,访问index.html ,观察输出:
sun.nio.ch.ChannelInputStream@274a058c

这个测试代码为什么要加一句

	System.out.println(resource.getInputStream()); 

呢?这里要填一个坑,咱们知道,不管是DeafultResoureLoader还是FileSystemResourceLoader也好,都是ResourceLoader的子子孙孙,ResourceLoader中 getResource(String location);方法有一个现象就是该方法返回的Resource实例并不保证该Resource一定是存在的!重要的话再说一遍!该方法返回的Resource实例并不保证该Resource一定是存在的!,所以要加一个输出看看是否真的加载到了,咱们实际使用时一定要用resource.exists()判断一下!

当然,咱们已可以皮一下,非要传个相对路径进来,像这样:

@Controller
public class LoginController  {
				
	@RequestMapping(value="/index.html")
	public String loginPage() throws IOException {		
		ResourceLoader resourceLoader=new FileSystemResourceLoader();
		Resource resource=resourceLoader.getResource("test.xml");
		System.out.println(resource.getInputStream()); 
				
		return "index";
	}	  
}

启动服务器,访问index.html,观察输出:

[INFO] Restart completed at Fri Jul 06 10:48:31 CST 2018
[WARNING] /chapter3/index.html java.nio.file.NoSuchFileException: test.xml

发现挂了,实际上test.xml是放在classpath下的,假如这么写肯定是没问题的,

Resource resource=resourceLoader.getResource("classpath:test.xml");

那咱们非要用相对路径,又非要用FileSystemResourceLoader咋办?咱们看一下FileSystemResourceLoader是如何实现的:

	/**
	 * Resolve resource paths as file system paths.
	 * <p>Note: Even if a given path starts with a slash, it will get
	 * interpreted as relative to the current VM working directory.
	 * @param path the path to the resource
	 * @return the corresponding Resource handle
	 * @see FileSystemResource
	 * @see org.springframework.web.context.support.ServletContextResourceLoader#getResourceByPath
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		if (path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemContextResource(path);
	}

如果path以斜杠开头,会把斜杠截取掉,这段代码说明就算咱们传个斜杠打头的进来,也会被转成相对路径,只不过该方法的注释信息已经告诉咱们了,如果是个相对路径的话,相对的就是虚拟机的工作目录,也就是说该项目的根目录,咱们的这个测试项目叫chapter3,现在chapter3就是虚拟机的工作目录,因此咱们可以这么写:

@Controller
public class LoginController  {
				
	@RequestMapping(value="/index.html")
	public String loginPage() throws IOException {
		
		ResourceLoader resourceLoader=new FileSystemResourceLoader();
		Resource resource=resourceLoader.getResource("target\\classes\\test.xml");
		System.out.println(resource.getInputStream()); 
				
		return "index";
	}	  
}
启动服务器,访问index.html,观察输出:
[INFO] Restart completed at Fri Jul 06 10:59:47 CST 2018
sun.nio.ch.ChannelInputStream@783ccd29
成功了,不过是不是感觉这种写法怪怪的?反正我觉得是,所以咱们的总结就是,使用FileSystemResourceLoader加载资源时,最好给出资源的绝对路径,如果使用“\”打头的或者是相对路径,可能会有坑。也由此得出一个结论,已斜杠打头资源路径指的是项目的根路径,或者说jvm的启动路径等等。普通的java项目就是项目路径,web项目就要看服务器的启动路径了,tomcat是bin,本例中咱们用的是jetty,咱们知道,jetty就是个jar包,用的时候扔到项目的classpath里就可以了,所以咱们测试项目的jvm启动路径就是chapter3 。是不是挺麻烦,是不是有点绕,所以除非有特殊原因必须使用FileSystemResourceLoader,否这咱们还是老实巴交的用classpath吧!

以上纯属个人理解,如有错误,还望读多指正,非常感谢!!!

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐