概述:
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStramHandler)来处理不同来源的资源读取逻辑,一般handler的类型使用不同的前缀(协议,Protocol)来识别,如“file:”,“http:”,“jar:”等,然而URL没有默认定义相对Classpath或ServletContext等资源的handler,虽然可以注册自己的URLStreamHandler来解析特定的URL前缀(协议),比如”classpath:”,然而需要了解URL实现机制。因而Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源:
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等,相关类图如下所示:
Resource
其中,最常用的有四个:
- ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
- FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
- ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
- UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。
ClassPathResource
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
通过ClassLoader#getResourceAsStream(String);Class#getResourceAsStream(String)获得流信息
ClassLoader#getResourceAsStream(String)的目录为class
Class#getResourceAsStream(String)的目录为当前Class文件目录
TestResource.class.getClassLoader().getResourceAsStream("")
TestResource.class.getClassLoader().getResourceAsStream("/")
TestResource.class.getResourceAsStream("")
TestResource.class.getResourceAsStream("/")
输出
file:/E:/Workspaces/base_Java/target/classes/
null
file:/E:/Workspaces1/myProject/base_Java/target/classes/com/code/resource/
file:/E:/Workspaces1/myProject/base_Java/target/classes/
FileSystemResource
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
通过创建FileInputStream输入流获得流信息
ServletContextResource
@Override
public InputStream getInputStream() throws IOException {
InputStream is = this.servletContext.getResourceAsStream(this.path);
if (is == null) {
throw new FileNotFoundException("Could not open " + getDescription());
}
return is;
}
通过ServletContext#getResourceAsStream返回资源文件的读取字节流
在web工程中,我们一般来说,是不能采取类加载器读取webRoot(webapp目录下)目录下配置文件的通过SevletContext#getResourceAsStream(“/imgs/test.properties”)获得输入流或通过SevletContext#getRealPath(“/imgs/Sunset.jpg”)获得绝对路径
UrlResource
@Override
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
UrlResource 封装了java.net.URL,它能够被用来访问任何通过URL可以获得的对象,例如:文件、HTTP对象、FTP对象等。
ResourceLoader
DefaultResourceLoader
@Override
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);
}
}
}
在Spring里面还定义有一个ResourceLoader接口,该接口中只定义了一个用于获取Resource的getResource(String location)方法。DefaultResourceLoader在获取Resource时:首先判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的ClassPathResource;否则就把它当做一个URL来处理,封装成一个UrlResource进行返回;如果当成URL处理也失败的话就把location对应的资源当成是一个ClassPathResource进行返回。
PathMatchingResourcePatternResolver
解决路径下有一个或多个资源文件,源路径可能是一个简单的路径或者可能含有特殊的{classpath*:}前缀和或Ant-style正则表达式(请看 org.springframework.util.AntPathMatcher类)
Resource[] re = pmrp.getResources("com/spring/**/*.class");
for(int i = 0;i < re.length;i++) {
System.out.println(re[i]);
}
输出class/com/spring下的所有文件:
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest.class]
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest2.class]
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest3.class]
Ant-style正则表达式:
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录例如:
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, > /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/**/*.jsp 匹配(Matches)任何的.jsp 文件