本文共 8104 字,大约阅读时间需要 27 分钟。
JDK操纵底层资源基本就是 java.net.URL 、java.io.File 等。处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性,正因为这样,Spring开发者抽象了一个Resource接口 ,提供了更强大的访问外部资源的能力。
上一章中的PropertySource表示Spring内部的key-value资源,而Resource表示一个外部资源。PropertySource可以来源于一个Resource。Resouce接口继承了 InputStreamSource.
public interface InputStreamSource { InputStream getInputStream() throws IOException; }
getInputStream()方法:每次调用都将返回一个当前资源对应的java.io. InputStream字节流,调用者在使用完毕后必须关闭该资源
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(); }
exists():用于判断对应的资源是否真的存在。
isReadable():用于判断对应资源的内容是否可读。需要注意的是当其结果为true的时候,其内容未必真的可读,但如果返回false,则其内容必定不可读。 isOpen():用于判断当前资源是否代表一个已打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄露。该方法主要针对于实现类InputStreamResource,实现类中只有它的返回结果为true,其他都为false。 getURL():返回当前资源对应的URL。如果当前资源不能解析为一个URL则会抛出异常。如ByteArrayResource就不能解析为一个URL。 getURI():返回当前资源对应的URI。如果当前资源不能解析为一个URL则会抛出异常。 getFile():返回当前资源对应的File。如果当前资源不能以绝对路径解析为一个File则会抛出异常。 contentLength() :资源的长度 lastModified:返回当前Resource代表的底层资源的最后修改时间。 createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/abc/”则createRelative(“xyz.txt”)将返回表文件资源“d:/abc/xyz.txt” getFilename:返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源将返回“”,因为只返回文件路径。 getDescription:返回当前Resource代表的底层资源的描述信息。抛开Resource接口及其子类复杂的类结构图。我们看一下它的主要实现类。
实现了Resource接口,是大多数Resource的实现类的基类,提供了很多通用的方法。
比如exists方法会检查是否一个文件或者输入流能够被打开。isOpen永远返回false。”getURL()” 和”getFile()”方法会抛出异常。 toString将会返回描述信息。ClassPathResource代表classpath路径的资源,将使用给定的Class或ClassLoader进行加载classpath 资源 。“isOpen”永远返回false,表示可多次读取资源。
第一种 使用Class对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。 参数既可以是当前class文件相对路径(以文件夹或文件开头),也可以是当前class文件的绝对路径(以“/”开头,相对于当前classpath根目录)
第二种 使用ClassLoader对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。参数只能是绝对路径,但不以“/”开头ClassPathResource加载资源替代了Class类和ClassLoader类加载类路径资源方法,提供一致的访问方式。
private final String path;private ClassLoader classLoader;private Class clazz;
public ClassPathResource(String path):使用ClassUtils.getDefaultClassLoader()指定例变量classLoader
public ClassPathResource(String path, ClassLoader classLoader): 指定例变量classLoader public ClassPathResource(String path, Class< ?> clazz):指定实例变量Class< ?> clazz@Overridepublic 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;}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); }}
通过getURL()和resolveURL()可知,获取URL资源的顺序为:
如果存在实例变量this.clazz ,则使用Class对象的getResource获取资源 不存在clazz,则通过this.classLoader的 getResource获取资源 不存在this.classLoader则使用 ClassLoader.getSystemResource获取资源。 ++获取资源流与获取资源URL的方法一致可用来获取文件系统里面的资源。我们可以通过对应资源文件的文件路径来构建一个FileSystemResource。FileSystemResource还可以往对应的资源文件里面写内容,当然前提是当前资源文件是可写的,这可以通过其isWritable()方法来判断。FileSystemResource对外开放了对应资源文件的输出流,可以通过getOutputStream()方法获取到。
可用来代表URL对应的资源,它对URL做了一个简单的封装。通过给定一个URL地址,我们就能构建一个UrlResource。继承自AbstractResource,ByteArrayResource代表byte[]数组资源,对于“getInputStream”操作将返回一个ByteArrayInputStream。
public static void main(String[] args) throws Exception { Resource resource=new ByteArrayResource("hello world".getBytes()); //1.getInputStream() InputStream inputStream= resource.getInputStream(); byte[] bytes=new byte[inputStream.available()]; inputStream.read(bytes); System.out.println(new String(bytes));//hello world inputStream.close(); //2.其他方法 System.out.println(resource.isOpen());//false System.out.println(resource.contentLength());//11 //... //3.抛出异常方法 lastModified() getFile()...等方法 //System.out.println(resource.lastModified()); //System.out.println(resource.getFile()); }
是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和getResourceAsStream()方法来获取资源的。
是针对于输入流封装的资源,它的构建需要一个输入流。 对于“getInputStream ”操作将直接返回该字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。
UrlResource代表URL资源,用于简化URL资源访问。
UrlResource一般支持如下资源访问: -http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”); -ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”); -file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”);在Spring里面还定义有一个ResourceLoader接口,该接口中只定义了一个用于获取Resource的getResource(String location)方法。这个接口的作用是判断传入的Resource相关字符串(位置,URL等),根据location参数返回不同的Resource。
ResourceLoader可以看做是Resource的工厂类DefaultResourceLoader是ResourceLoader最基本的实现类。
DefaultResourceLoader在获取Resource时采用的是这样的策略: -首先判断指定的location是否含有“/”前缀有则返回ClassPathContextResource; -然后判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的ClassPathResource; -否则就把它当做一个URL来处理,封装成一个UrlResource进行返回; -如果当成URL处理也失败的话就把location对应的资源当成是一个ClassPathContextResource进行返回。 源码如下:public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith("/")) { return getResourceByPath(location); //返回new ClassPathContextResource(path, getClassLoader()); } else if (location.startsWith("classpath:")) { return new ClassPathResource(location.substring("classpath:".length()), getClassLoader()); } else { try { // 转换为URL... URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // url转换失败 return getResourceByPath(location); //返回new ClassPathContextResource(path, getClassLoader()); } }}
测试代码:
ResourceLoader loader = new DefaultResourceLoader();Resource resource1 = loader.getResource("/application.properties");System.out.println(resource1.getClass());//class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource Resource resource2 = loader.getResource("classpath:application.properties");System.out.println(resource2.getClass());// class org.springframework.core.io.ClassPathResource Resource resource3 = loader.getResource("http://blog.csdn.net/isea533/article/details/42102297");System.out.println(resource3.getClass());//class org.springframework.core.io.UrlResource Resource resource4 = loader.getResource("dsfsdfsdfawewrwrwr");System.out.println(resource4.getClass());//class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource
Spring提供ResourcePatternResolver接口来加载多个Resource,该接口继承了ResourceLoader并添加了“Resource[] getResources(String locationPattern)”用来加载多个Resource:
classpath: 用于加载类路径(包括jar包)中的一个且仅一个资源;
classpath* : 用于加载类路径(包括jar包)中的所有匹配的资源,可使用Ant路径模式。Ant路径通配符支持“?”、“*”、“**”,注意通配符匹配不包括目录分隔符“/”:“?”:匹配一个字符,如“config?.xml”将匹配“config1.xml”;“*”:匹配零个或多个字符串,如“cn/*/config.xml”将匹配“cn/javass/config.xml”,但不匹配匹配“cn/config.xml”;而“cn/config-*.xml”将匹配“cn/config-dao.xml”;“**”:匹配路径中的零个或多个目录,如“cn/**/config.xml”将匹配“cn /config.xml”,也匹配“cn/javass/spring/config.xml”;而“cn/javass/config-**.xml”将匹配“cn/javass/config-dao.xml”,即把“**”当做两个“*”处理。
大多数ApplicationContext都实现了ResourceLoader接口,所以它们也具有载入资源