spring资源操作(Resource)
Java的标准java.net.URL类和各种URL前缀的处理标准处理程序无法满足所有对low-level资源的访问,比如:没有标准优化的URL实现可用于访问需要从类路径或相对于ServletContext获取的资源。并缺少某些Spring所需要的功能。如检测某资源是否存在等。而Spring的Resource声明了访问low-level资源的能力。
Spring Resource
Resource接口
Resource接口是Spring资源访问策略的抽象,它本身不提供任何资源访问实现,具体的资源访问由该接口的实现类完成(每一个实现类代码表一种资源访问策略)。Resource一般包含这些实现类:
UrlResource
、ClassPathResource
、FileSystemResource
、ServletContextResource
、InputStreamResource
和ByteArrayResource
UrlResource访问网络资源
Resource
的一个实现类,用来访问网络资源,它支持URL的绝对路径
http
:该前缀用于访问基于HTTP协议的网络资源。
ftp
:该前缀用于访问基于FTP协议的网络资源
file
:该前缀用于从文件系统中读取资源。
示例
//UrlResource访问网络资源
public class UrlResourceDemo {
public static void loadUrlResource(String path){
//创建Resource实现类的对象UrlResource
try {
UrlResource url = new UrlResource(path);
//获取资源信息
System.out.println("url.getURL()" + url.getURL());
System.out.println("url.getFilename() = " + url.getFilename());
System.out.println("url.getDescription() = " + url.getDescription());
System.out.println("url.getInputStream() = " + url.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//http前缀
UrlResourceDemo.loadUrlResource("http://www.baidu.com");
/*
url.getURL()http://www.baidu.com
url.getFilename() =
url.getDescription() = URL [http://www.baidu.com]
url.getInputStream() = sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@7591083d
* */
//file前缀,下面的写法需要注意文件要放在根路径下(spring6),也可以写如绝对路径
loadUrlResource("file:louis.txt");
/*
* url.getURL()file:louis.txt
url.getFilename() = louis.txt
url.getDescription() = URL [file:louis.txt]
url.getInputStream() = java.io.BufferedInputStream@7eda2dbb
* */
}
}
ClassPathResource访问路径下资源
ClassPathResource用来访问类加载路径下的资源,相对于其他的Resource实现类,其主要优势是方便访问类加载路径里的资源,尤其对于web应用,ClassPathResource可自动搜索位于classes下的资源文件,无需使用绝对路径
示例
package com.louis.resource;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
/**
* @author XRY
* @date 2023年06月28日18:40
*/
//访问类路劲下的资源
public class ClassPathResourceDemo {
public static void loadClassPathResource(String url){
//创建对象
ClassPathResource classPathResource = new ClassPathResource(url);
System.out.println("classPathResource.getFilename() = " + classPathResource.getFilename());
System.out.println("classPathResource.getDescription() = " + classPathResource.getDescription());
//获取文件内容
try {
InputStream inputStream = classPathResource.getInputStream();
byte[] b = new byte[1024];
int len = 0;
while((len = inputStream.read(b)) != -1){
System.out.println(new String(b, 0, len));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//是相对路径,可以放在resources下
loadClassPathResource("louis.txt");
/*
* classPathResource.getFilename() = louis.txt
classPathResource.getDescription() = class path resource [louis.txt]
Hi,louis
* */
}
}
FileSystemResource访问文件系统资源
Spring提供的FileSystemResource类用于访问文件系统资源,使用FileSystemResource来访问文件系统资源并没有太大的优势,因为java提供的File类也可以用于访问文件系统资源。
示例
public class FileSystemResourceDemo {
public static void loadFileResource(String path){
//创建对象
FileSystemResource fileResource = new FileSystemResource(path);
System.out.println("fileResource.getFilename() = " + fileResource.getFilename());
System.out.println("fileResource.getDescription() = " + fileResource.getDescription());
//创建输入流对象
try {
InputStream inputStream = fileResource.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len=inputStream.read(bytes))!=-1){
System.out.println(new String(bytes, 0, len));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//相对路径或绝对路径
loadFileResource("C:\\Users\\XRY\\Desktop\\text.txt");
/*
* fileResource.getFilename() = text.txt
fileResource.getDescription() = file [C:\Users\XRY\Desktop\text.txt]
测试fileSystemResource
* */
}
}
ServletContextResource
这是ServletContext资源的Resource实现,它解释相关Web应用程序根目录中的相对路径,它始终支持流(stream)访问和URL访问,但只有在扩展web应用程序存档且资源实际位于文件系统上时才允许java的io,File访问,无论他是在文件系统上扩展还是直接从JAR或其他地方访问,实际上都是依赖Servlet容器。
InputStreamResource
InputStreamResource是给定的输入流InputStream的Resource实现,它的使用场景在没有特定的资源实现的时候使用(感觉和@Component的使用场景很相似)。与其他Resource实现相比,这是已经打开资源的描述符,因此,它的isOpen()方法返回true,如果需要将资源描述符保留在某处或者需要多次读取流,建议不要使用。
ByteArrayResource
字节数组的Resource实现类。通过给定的数组创建一个ByteArrayInputStream。它对于从任何给定的字节数组加载内容非常有用,而无需求助于单次使用的InoutStreamResource.
ResourceLoader接口
Spring ResourceLoader概述
ResourceLoader
:该接口实现类的实例可以获得一个Resource实例
ResourceLoaderAware
:该接口实现类的实例将获得一个ResourceLoader的引用
ResourceLoader
在ResourceLoader接口主要定义了一个方法:
getResource(String location)
:该接口,用于返回一个Resource实例。
ApplicationContext实现类都实现ResourceLoader接口,因此ApplicationContext可直接获取Resource实例。
示例
@Test
public void testClassPathXmlApplicationContext() {
ApplicationContext context = new ClassPathXmlApplicationContext();
Resource resources = context.getResource("louis.txt");
System.out.println(resources.getFilename());
/*
* louis.txt
* */
}
@Test
public void testFileSystemApplicationContext(){
ApplicationContext context = new FileSystemXmlApplicationContext();
Resource resource = context.getResource("louis.text");
System.out.println(resource.getFilename());
/*
* louis.text
* */
}
Spring采用ApplicationContext相同的策略来访问资源,如果ApplicationContext是FileSystemApplicationContext,返回的就是FileSystemResource实例,如果ApplicationContext是ClassPathXmlApplicationContext,返回的就是ClassPathResource
总结:
当spring应用需要进行资源访问时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource()方法获取资源,ResourceLoader将会负责选择Resource实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分开来,另外,使用ApplicationContext访问资源时,可通过不同前缀指定强制使用指定的ClassPathResource、FileSystemResource等实现类。
ResourceLoaderAware
ResourceLoaderAware接口实现类的实例将获得一个ResourceLoader的引用,ResourceLoaderAware接口也提供了一个setResourceLoader()方法,该方法由spring容器负责调用,spring容器会将一个ResourceLoader对象作为该方法的参数传入。
如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中,Spring容器会将自身当成ResourceLoader作为setResourceLoader()方法的参数传入。由于ApplicationContext的实现类都实现了ResourceLoader接口,Spring容器自身完全可作为ResourceLoader使用。
测试通过get、set方法创建的ResourceLoader和Spring传入的ResourceLoader是否为同一对象
自主创建
public class TestResourceLoaderAware implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testResourceLoaderAware" class="com.louis.resourceloaderaware.TestResourceLoaderAware"></bean>
</beans>
测试
@Test
public void testAware(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
TestResourceLoaderAware loaderAware = applicationContext.getBean("testResourceLoaderAware", TestResourceLoaderAware.class);
ResourceLoader resourceLoader = loaderAware.getResourceLoader();
System.out.println(resourceLoader == applicationContext);
/*true*/
}
Resource作为属性
Bean实例需要访问资源,有如下的两种方式:
1、代码中获取Resource实例
2、使用依赖注入
对于第一种方式,当程序获取Resource实例时,总需要提供Resource所在的位置,这意味着资源所有的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序,因此,通常建议使用方式二,让Sprig为Bean实例依赖注入资源。
Spring为Bean实例依赖注入资源
步骤
1、创建ResourceBean类
public class ResourceBean {
private Resource resource;
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public void parse(){
System.out.println(resource.getFilename());
System.out.println(resource.getDescription());
}
}
2、使用配置文件(bean-di.xml)引入资源
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="resourceBean" class="com.louis.di.ResourceBean">
<property name="resource" value="louis.txt"></property>
</bean>
</beans>
3、测试
public class TestResourceBean {
@Test
public void testResourceBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
ResourceBean resource = context.getBean("resourceBean", ResourceBean.class);
resource.parse();
/*
* louis.txt
class path resource [louis.txt]
* */
}
}
应用程序上下文和资源路径
概述
不管以怎样的方式创建ApplicationContext实例,都需要为Application指定配置文件,Spring允许使用一份或多分XML配置文件,当程序创建ApplicationContext实例时,通常也是以Resource的方式访问配置文件,所以ApplicationContext完全支持ClassPathResource、FileSystemResource、ServletContextResource等资源的访问方式。
ApplicationContext确定资源访问策略通常有两种方式
- 使用ApplicationContext实现类指定访问路径
- 使用前缀指定访问路径
ApplicationContext实现类执行访问路径
创建ApplicationContext对象时,通常可以实现如下实现类:
ClassPathXMLApplicationContext
:对应使用ClassPathResource进行资源访问FileSystemXmlApplicationCOntext
:对应使用FileSystemResource进行资源访问XmlWebApplicationContext
:对应使用ServletContextResource进行资源访问
当使用ApplicationContext不同实现类时,就意味着Spring使用响应的资源访问策略,之前写的就是这种方式。
使用前缀指定访问路径
测试类
public class TestDemo {
public static void main(String[] args){
//classpath:就表示通过前缀查找类路径下的资源
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean-prefix.xml");
Resource resource = context.getResource("louis.txt");
System.out.println("resource = " + resource.getDescription());
/*resource = class path resource [louis.txt]*/
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="prefixResource" class="com.louis.prefix.TestDemo"></bean>
</beans>
classpath通配符使用
classpath*:
前缀提供了加载多个XMl配置文件的能力,当使用classpath*:
前缀来指定XML配置文件时,系统将搜索类加载路径,找到与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("classpath\*:bean.xml");
如果不采用classpath*:前缀,而是改为classpath:前缀,Spring则只加载第一个符合条件的XML文件
注意:
classpath*:
前缀仅对ApplicationContext有效,实际情况是,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath*:
前缀不可用于Resource。
通配符其他使用
一次加载多个配置文件的方式:指定配置文件时使用通配符
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean*.xml");
Spring允许将classpath*:
前缀和通配符结合使用
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:bean*.xml");