目录
- 资源处理和使用
- 1. 资源接口
- 2. Spring内置资源实现类
- 3. ResourceLoader接口
- 4. 资源依赖
资源处理和使用
URI(Universal Resource Identifier) :统一资源标志符
URL(Universal Resource Locator):统一资源定位符
URN(Universal Resource Name):统一资源名称
URI可以是URL、URN,或者是(URL + URN),所以可以说URI是其他两个的父级。
资源包括XML、Properties、File、二进制流等等。但Java的标准java.net.URL类库和用于各种URL前缀的标准处理器并不足以访问所有的底层资源,同时Spring如果直接使用这些底层API,工作量将会是巨大的,所以Spring对于资源访问进行了统一的抽象处理。
1. 资源接口
Spring提供一个Resource接口,用于统一的访问底层资源。它实质上是继承自InputStreamSource 接口。
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
@Nullable
String getFilename();
String getDescription();
}Resource接口一些重要的方法如下:
-
exists():返回一个布尔值,表示该资源是否以物理形式实际存在。 -
isOpen():返回一个布尔值,表示该资源是否进行了开启流的处理。如果是true,则InputStream仅能读取一次,不能多次读取,然后关闭以防止资源泄漏。除InputStreamResource外,对所有通常的资源实现都返回false。 -
getInputStream():父类方法,定位并打开此资源,返回从该资源读取的InputStream,每次调用都会返回新的InputStream,需要调用者手动关闭。 -
getDescription():返回当前资源的描述信息,通常是该资源的完全限定文件名或URL。
其他方法:
-
isReadable():返回一个布尔值,表示该资源是否可读。 -
isFile():返回一个布尔值,表示该资源是否为文件。 -
getURL():如果该资源能解析为URL,则返回URL,否则抛出异常。 -
getURI():如果该资源能解析为URI,则返回URI,否则抛出异常。 -
getFile():如果该资源能解析为File,则返回File,否则抛出异常。 -
contentLength():返回该资源的长度。 -
lastModified():返回该资源的最后修改时间。 -
createRelative(String relativePath):创建相对于此资源路径的资源,并返回该资源。 -
getFilename():返回该资源的文件名。
2. Spring内置资源实现类
- URLResource
该资源实现类通过对java.net.URL进行包装,简化URL的资源访问,可用来访问通过URL直接访问的任何对象,如文件、HTTP、FTP等。所有URL都有标准化的字符串表示,因此可以使用标准化的前缀表示不同类型的URL(file:、http:、ftp:等)。
UrlResource urlResource = new UrlResource("file:///C:/xxx/xxx/xxx/123.txt");URLResource是通过Java代码显式使用构造方法创建的,一般情况下,我们在调用字符串路径作为参数的某种API方法创建特定资源时,URLResource会隐式地创建。对于后者,则是通过PropertyEditor来最终决定创建哪种类型的资源实现,如果字符串包含前缀,它将通过该前缀创建合适类型的资源实现(如果是classpath:前缀将会创建ClassPathResource),如果不能识别该前缀,则假定该字符串式标准URL字符串,从而确定创建URLResource。
PropertyEditor接口可以实现字符串和对象之间的转换,如转换为日期、URL、集合、类、数组、地区、文件等,这些都是通过内置好的PropertyEditor实现类完成,当然也可以通过接口自定义实现类并注册使用。
- ClassPathResource
该资源实现类通过ClassLoader或给定类来加载资源,简化类路径下的资源访问。ClassPathResource支持java.io.File的解析,但仅支持存在于文件系统中的资源,不支持存在于Jar包且扩展到文件系统中的类路径资源。并且,所有的资源实现类也都支持java.net.URL的解析,即能使用getURL()。
ClassPathResource resource = new ClassPathResource("123.txt");ClassPathResource是通过Java代码显式使用构造方法创建的。一般情况下,我们在调用字符串路径作为参数的某种API方法创建特定资源时,ClassPathResource会隐式地创建。对于后者,则是通过PropertyEditor识别该字符串参数上的前缀classpath:,从而确定并创建ClassPathResource。
classpath: 和 classpath: 区别*
classpath:表示从类路径下加载资源,但只限于第一个类路径。classpath*:表示从多个类路径下加载相同名称的资源(也包括jar包中的),因此加载效率会很慢,请谨慎使用。
- FileSystemResource
java.io.File和java.nio.file.Path句柄的资源实现,支持解析File和URL。
- ServletContextResource
针对于web下ServletContext资源访问的资源实现(入参资源位置相对于WEB-INF文件夹下)。它支持IO和URL资源访问。
- InputStreamResource
通过给定InputStream的资源实现,只有在没有适合的资源实现类时才使用。由于isOpen()返回为true,所以如需保存资源描述符或者需多次开启流,请不要使用它(一般考虑使用ByteArrayResource)。
- ByteArrayResource
**通过给定字节数组的资源实现,创建一个ByteArrayInputStream。**它可以从重复读取数组资源,所以不必使用一次性的InputStreamResource。
3. ResourceLoader接口
通过ResourceLoader接口可以返回资源实例和类加载器。
public interface ResourceLoader {
//实际为字符串 classpath:
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
//返回指定资源路径的资源句柄,支持URL、类路径和相对路径
Resource getResource(String location);
//返回该这个ResourceLoader使用的类加载器
@Nullable
ClassLoader getClassLoader();
}再来看看它的子接口及实现类:

可以看出,所有的容器上下文都实现了ResourceLoader,因此,我们可以通过容器上下文对象获取资源实例。
当你使用特定类型的容器上下文来获取资源时,若指定路径没有前缀类型,则会返回一个适合于该容器上下文的资源类型,如通过ClassPathXmlApplicationContext会得到ClassPathResource类型的资源,WebApplicationContext会得到ServletContextResource类型的资源。因此,你可以根据需求使用特定类型的容器上下文来加载资源。
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ac.getResource("org/data/123.txt");从容器上下文的构造方法也可以看出,路径字符串若没有前缀,也是根据其特定类型的容器上下文来加载资源的,资源类型即为ClassPathResource。
你也可以加入前缀来强制使用某种资源实现,从而取代容器上下文的默认资源加载类型。
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ac.getResource("file:///data/123.txt");前缀 | 例子 | 描述 |
classpath: | classpath:org/config/myConfig.xml | 从类路径加载 |
file: | file:///D:/…/…/myConfig.xml | 从文件系统中通过URL加载 |
http: | 从网络通过URL加载 | |
无 | /config/myConfig.xml | 由容器上下文判断加载方式 |
获取ResourceLoader方式
- 组件可通过实现
ResourceLoaderAware接口来获取其引用。 - 通过自动装配注入
ResourceLoader(推荐更加灵活的注解式自动装配)
实际上,由于容器上下文本身就已经实现了ResourceLoader接口,所以我们还可以使用ApplicationContextAware来获取其引用。对于只想专门使用ResourceLoader的话还是建议使用ResourceLoaderAware,代码只会耦合到该接口,而不是更大的ApplicationContext接口。
4. 资源依赖
- 若你的需求是通过动态确定路径来加载资源,那么使用
ResourceLoader接口是最好的选择。
public class MyResourceLoaderBean implements ResourceLoaderAware {
private ResourceLoader loader;
public void setResourceLoader(ResourceLoader resourceLoader) {
this.loader = resourceLoader;
}
public Resource getResource(String path){
return loader.getResource(path);
}
}
//主入口方法
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
MyResourceLoaderBean myResourceLoaderBean = (MyResourceLoaderBean) ac.getBean("myResourceLoaderBean");
System.out.println(myResourceLoaderBean.getResource("classpath:123.txt"));
}- 如果你的资源是静态不变的,那么只需提供其路径让它被直接注入即可。
PropertyEditor通过其字符串路径及前缀(若无前缀,会根据其容器上下文来选择合适的资源实现),隐式的创建ClassPathResource并注入。
@Component
public class MyBean {
@Value("classpath:123.txt")
private Resource resource;
public void setResource(Resource resource) {
this.resource = resource;
}
}
















