Spring框架大量使用了Resource来访问底层资源。
Resource接口提供的方法:
- getInputStream():定位打开资源,返回资源对应的输入流,每次调用都返回新的输入流,调用者必须负责关闭输入流。
- exists():返回Resource所指的资源是否存在。
- isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束时应该显示关闭,防止资源泄露。
- getDescription():返回资源的描述星星,用于资源处理出错时输出该信息,通常是全限定名或实际URL。
- getFile():返回资源对应的File对象。
- getURL:返回资源对应的URL对象。
Resource实现类:
Spring提供的Resource接口的大量实现类:
- UrlResource:访问网络资源的实现类。
- ClassPathResource:访问类加载路径里资源的实现类。
- FileSystemResource:访问文件系统里资源的实现类。
- ServletContextResource:访问相对于ServletContext路径下的资源的实现类。
- InputStreamResource:访问输入流资源的实现类。
- ByteArrayResource:访问字节数组资源的实现类。
1、访问网络资源:
通过UrlResource类实现。
package test;
import java.net.MalformedURLException;
import org.springframework.core.io.UrlResource;
public class UrlResourceTest {
public static void main(String[] args) throws Exception{
//创建对象,指定从文件系统读取资源
UrlResource ur=new UrlResource("file:books.xml");
//获取资源的简单信息
System.out.println(ur.getFilename());
System.out.println(ur.getDescription());
}
}
通过使用file:前缀可访问本地磁盘资源,如果需要访问网络资源,可以使用如下两个前缀:
- http:用于访问基于HTTP协议的网络资源。
- ftp:用于访问基于FTP的网络资源
2、访问类加载路径下的资源:
ClassPathResource用来访问类加载路径下的资源,相对于其他的Resource实现类,其主要优势是方便访问类加载路径下的资源,尤其对web应用,ClassPathResource可自动搜索位于WEB-INF/classes下的资源文件,无需使用绝对路径访问。
public class ClassPathResourceTest{
public static void main(String[] args){
//创建一个Resource对象,从类加载路径下读取资源
ClassPathResource cr=new ClassPathResource("books.xml");
//获取资源的简单信息。
..........
}
}
ClassPathResource实例可使用ClassPathResource实例可使用ClassPathResource构造器显示地创建,但更多时候它都是隐式创建的。当执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串参数中包含classpath:前缀后,系统将会自动创建ClassPathResource对象。
3、访问文件系统的资源:
public class ClassPathResourceTest{
public static void main(String[] args){
//默认从文件系统的当前路径加载books.xml资源
FileSystemResource fr=new FileSystemResource("books.xml");
//获取资源的简单信息。
..........
}
}
与前两种使用Resource进行资源访问的区别在于:资源字符串确定的资源,位于本地系统类,而且无需使用任何前缀。
4、访问应用相关资源:
public class ClassPathResourceTest{
public static void main(String[] args){
//从WEB Context下的WEB-INF路径下读取books.xml资源
ServletContextResource src=new ServletContextResource("WEB-INF/books.xml");
//获取资源的简单信息。
..........
}
}
当程序试图直接通过File来访问Web Context下相对路径下的资源时,应该先使用ServletContext的getRealPath()方法来取得资源的绝对路径,再以该绝对路径来创建File对象。
5、访问字节数组资源:
InputStreamResource用于访问二进制输入流资源,InputStreamResource是一个总是被打开的Resource,所以isOpen()方法总是返回true。如果需要多次读取某个流,就不要使用InputStreamResource,创建InputStreamResource应该提供一个InputStream参数。InputStreamResource虽然是适应性很广的的Resource实现,但效率不好,而应尽量使用ByteArrayResource或FileSystemResource代替它。对于需要采用InputStreamResource访问的资源,可先从InputStream流中读出字节数组,然后以字节数组来创建ByteArrayResource。
ResourceLoader接口和ResourceLoaderAware接口:
Spring提供如下两个标志行接口:
- ResourceLoader:该接口实现类的实例可以获得一个Resource实例。
- ResourceLoaderAware:该接口实现类的实例可以获得一个ResourceLoader的引用。
ResourceLoader接口里的方法:
- Resource getResource(String location):该接口仅包含这个方法,该方法用于返回一个Resource实例。ApplicationContext的实现类都实现ResourceLoader接口,因此ApplicationContext可用于直接获取Resource实例。
某个ApplicationContext实例获取Resource实例时,默认采用与ApplicationContext相同的资源访问策略。
//通过ApplicationContext访问资源
Resource res=ctx.getResource("some/resource/path/myTemple.txt");
从上面的代码中无法确定Spring用哪个实现类来访问指定的资源,Spring采用和ApplicationContext相同的策略来访问资源。如果ApplicationContext是FileSystemXmlApplicationContext,res就是FileSystemResource实例;如果ApplicationContext是ClassPathXmlApplicationContext,res就是ClassSystemResource实例;如果ApplicationContext是XmlWebApplicationContext,res就是ServletContextResource实例;
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
public class ResourceLoaderTest {
//创建ApplicationContext实例
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
//res是ClassPathResource实例
Resource res=ctx.getResource("books.xml");
//获取资源的简单信息
......
}
也可以不理会ApplicationContext的实现类,强制使用指定的ClassPathResource、FileSystemResource等实现类,这可通过不同的前缀来指定。
//通过classpath:前缀,强制使用ClassPathResource
Resource res=ctx.getResource("classpath:beans.xml");
常见的前缀及对应的访问策略:
- classpath:以ClassPathResource实例访问类加载路径下的资源。
- file:以UrlResource实例访问本地文件系统的资源。
- http:以UrlResource实例访问基于HTTP协议的网络资源。
- 无前缀:有ApplicationContext的是眼泪来决定访问策略。
ResourceLoaderAware完全类似于Spring提供的BeanFactoryAware、BeanNameAware几口,ResourceLoaderAware也提供了一个setResourceLoader()方法,该方法将由Spring容器负责调用,Spring容器将一个ResourceLoader对象作为该方法的参数传入。如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中,Spring容器会将自身当成ResourceLoader作为setResourceLoader()方法的参数传入。由于ApplicationContext的实现类都实现了ResourceLoader接口,Spring容器本身完全可以作为ResourceLoader使用。
如下的Bean类实现了ResourceLoaderAware接口:
package test;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
public class TestBean implements ResourceLoaderAware {
private ResourceLoader rd;
/*
* 实现ResourceLoaderAware接口必须实现该方法
* 如果把该Bean部署在Spring容器中,该方法将会由Spring容器负责调用
* Spring容器调用该方法时,Spring会将自身作为参数传给该方法
* */
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
// TODO Auto-generated method stub
this.rd=resourceLoader;
}
//返回ResourceLoader对象的引用
public ResourceLoader getResourceLoader(){
return rd;
}
}
将该类部署到AppliactionContext中,然后使用如下主程序调用:
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ResourceLoader;
public class SpringTest {
public static void main(String[] args) {
//创建ApplicationContext容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
//获取容器中名为test的Bean实例
TestBean tb=ctx.getBean("test", TestBean.class);
//通过tb实例来获取ResourceLoader实例
ResourceLoader r1=tb.getResourceLoader();
//判断程序获得的ResourceLoader和容器是否相同
System.out.println(r1==ctx);
}
}
输出true,表明Spring容器确实是将自身注入到ResourceLoaderAware Bean中。
使用Resource作为属性:
当引用程序中的Bean实例需要访问资源时,Spring有更好的解决方法:直接利用依赖注入。如果Bean实例需要访问资源,有如下两种解决方案:
- 在代码中获取Resource实例。
- 使用依赖注入。
当程序获取Resource实例时,总需要提供Resource所在的位置,资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。因此,通常采用第二种方法,让Spring为Bean实例依赖注入资源。
public class TestBean{
private Resource res
//res的setter方法
public void setRes(Resource res){
this.res=res;
}
........
}
采用依赖注入,允许动态配置资源位置,无需将资源文件位置写入代码中,当资源文件位置发生变化时,直接修改配置文件即可。
在ApplicationContext中使用资源:
ApplicationContext确定资源访问策略通常有两种方法:
- 使用ApplicationContext实现类指定访问策略。
- 使用前缀指定访问策略。
1、使用ApplicationContext实现类指定访问策略:
- ClassPathXmlApplicationContext:对应使用ClassPathResource进行资源访问。
- FileSystemXmlApplicationContext:对应使用FileSystemResource进行资源访问。
- XmlWebApplicationContext:对应使用ServletContextResource进行资源访问。
2、使用前缀指定访问策略:
Spring通过使用http:、ftp:、classpath:等前缀来确定对应资源的访问策略。
如果程序需要使用ApplicationContext访问资源,建议显式采用对应的实现类来加载配置文件,而不是通过前缀来指定资源文件策略。
public class SpringTest{
public static void main(String[] args){
//通过类加载路径下的资源访问创建ApplicationContext,
//因为使用了classpath:前缀强制搜索类加载路径
ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath:beans.xml");
System.out.println(ctx);
//使用ApplicationContext加载资源,通过classpath:前缀指定访问策略
Resource r=ctx.getResource("classpath:books.xml");
//输出Resource描述
System.out.println(r.getDescription());
}
}
输出:
class path resource [books.xml]
由此可见,如果每次进行资源访问时都指定前缀,则系统会采用前缀相应的资源访问策略。
3、classpath*:前缀的用法:
classpath:前缀提供加载多个XML配置文件的能力,当使用classpath:前缀来指定XML配置文件时,系统将搜索类加载路径,找出所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext。
public class SpringTest{
public static void main(String[] args){
//使用classpath*:加载多个配置文件
ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath*:beans.xml");
System.out.println(ctx);
}
}
如果不采用classpath:前缀,而是改用classpath:前缀,Spring只按搜索路径加载一个符合条件的XML文件。classpath:前缀仅对ApplicationContext有效。实际情况时,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath:前缀不可用与Resource,使用classpath:前缀一次性访问多个资源是行不通的。**
通过配置文件时指定通配符也可以一次加载多个配置文件:
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans*.xml");
也可以将classpath:前缀和通配符结合使用:*
ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath*:beans*.xml");
4、file:前缀的用法:
public class SpringTest{
public static void main(String[] args){
//采用相对路径的写法
ApplicationContext ctx=new FileSystemXmlApplicationContext("beans.xml");
//采用绝对路径的写法
ApplicationContext ctx=new FileSystemXmlApplicationContext("/beans.xml");
System.out.println(ctx);
}
}
当FileSystemXmlApplicationContext作为ResourceLoader使用时,它会发生变化,FileSystemApplicationContext会简单的让所有绑定的FileSystemResource实例把绝对路径当成相对路径来处理。
ApplicationContext ctx=new FileSystemXmlApplicationContext("file:beans.xml");
ApplicationContext ctx=new FileSystemXmlApplicationContext("file:/beans.xml");
上面第一条语句访问相对路径下的beans.xml,第二条路径访问绝对路径下的beans.xml。相对路径以当前工作路径为起点,绝对路径以文件系统根路径为路径起点。