文章目录
用一个案例来引入:
ResourceLoader resourceLoader = new DefaultResourceLoader();
ClassPathResource resource = (ClassPathResource)resourceLoader.getResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
TestService1 testService1 = (TestService1)factory.getBean("TestService1");
testService1.print();
和我们常用的ApplicationContext不同,这个代码将ioc的构造细分开了,这样更加容易理解每个部分的工作流程。今天先学习Resource接口和ResourceLoader。也就是这个代码对应的前两句。
统一资源接口
在Spring使用的时候需要读入资源,比如bean.xml也是需要读入的资源。有加载资源的需求就有加载资源的策略。在Spring当中资源加载应当满足以下需求:
- 职能划分清楚。资源的定义和资源的加载应该要有一个清晰的界限;
- 统一的抽象。统一的资源定义和资源加载策略。资源加载后要返回统一的抽象给客户端,客户端要对资源进行怎样的处理,应该由抽象资源接口来界定。
Spring中的统一资源接口是Resource接口。在Spring中所有的资源都是通过Resource接口来抽象的。 它继承 org.springframework.core.io.InputStreamSource
接口。作为所有资源的统一抽象,Resource 定义了一些通用的方法,由子类 AbstractResource
提供统一的默认实现。定义如下:
public interface Resource extends InputStreamSource {
/**
*判断资源是否存在
*/
boolean exists();
/**
* 判断资源是否可读
*/
default boolean isReadable() {
return true;
}
/**
* 资源所代表的句柄是否被一个stream打开了
*/
default boolean isOpen() {
return false;
}
/**
* 是否为一个File
*/
default boolean isFile() {
return false;
}
/**
* 返回资源的url句柄
*/
URL getURL() throws IOException;
/**
* 返回资源的URI句柄
*/
URI getURI() throws IOException;
/**
* 返回File的句柄
*/
File getFile() throws IOException;
/**
* 返回一个ReadableByteChannel,这个用于nio类型的资源
*/
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 接口可以说是资源的顶级接口,它的默认实现类是AbstractResource类。这个体系中不同的资源提供了不同的具体实现,Resource的类图为:
- AbstractResource :Resource接口的默认实现类,里面包含了很多方法的默认实现,我们自定义Resource的时候可以直接继承这个类。
- FileSystemResource : 对
java.io.File
类型资源的封装 ,支持NIO类型。 - ByteArrayResource : 对字节数组提供的数据的封装。
- UrlResource :对
java.net.URL
类型资源的封装。内部委派 URL 进行具体的资源操作。 - ClassPathResource :class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。实例中使用的就是这个。
- InputStreamResource :将给定的 InputStream 作为一种资源的 Resource 的实现类。
AbstractResource
Resource的默认抽象实现:
public abstract class AbstractResource implements Resource {
/**
* 判断文件是否存在,若判断过程中出现异常,会关闭对应的流。
*/
@Override
public boolean exists() {
// getFile()是需要子类自己实现的
try {
return getFile().exists();
}
catch (IOException ex) {
// Fall back to stream existence: can we open the stream?
try {
getInputStream().close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean isReadable() {
return true;
}
/**
* This implementation always returns {@code false}.
*/
@Override
public boolean isOpen() {
return false;
}
/**
* This implementation always returns {@code false}.
*/
@Override
public boolean isFile() {
return false;
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to a URL.
*/
@Override
public URL getURL() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
}
/**
* This implementation builds a URI based on the URL returned
* by {@link #getURL()}.
*/
@Override
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to an absolute file path.
*/
@Override
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* 根据 getInputStream() 的返回结果构建 ReadableByteChannel.
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 获取资源长度,通过全部读一遍来获取资源的字节长度。
*/
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
try {
long size = 0;
byte[] buf = new byte[256];
int read;
while ((read = is.read(buf)) != -1) {
size += read;
}
return size;
}