java @resource,Spring Resource和ResourceLoader源码解析

 2023-09-28 阅读 29 评论 0

摘要:Spring用Resource接口抽象所有的底层资源,包括File、ClassPath、URL等。ResourceLoader接口是Resource的加载器,根据资源的路径/路径模式获取Resource实例。 Resource 接口定义 Resource接口的定义如下: public interface Resource extends InputStreamS

Spring用Resource接口抽象所有的底层资源,包括File、ClassPath、URL等。ResourceLoader接口是Resource的加载器,根据资源的路径/路径模式获取Resource实例。

Resource

接口定义

Resource接口的定义如下:

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;@NullableString getFilename();String getDescription();}

它继承自InputStreamSource接口,这个接口只有一个方法:

public interface InputStreamSource {InputStream getInputStream() throws IOException;}

这里比较重要的几个方法有:

  • getInputStream(),定位并且打开当前资源,返回该资源的一个InputStream。每次调用都会返回一个新的InputStream。
  • exists(),返回该资源是否物理存在。
  • getURL(),返回该资源的URL句柄。
  • getURI(),返回该资源的URI句柄。
  • isFile(),是否是文件。
  • getFile(),返回该资源的File句柄。

Resource接口的实现

Spring提供了多种Resource实现,我们可以直接使用。

FileSystemResource

java @resource。FileSystemResource是针对java.io.File的Resource实现类,其构造函数与getInputStream()方法如下:

public class FileSystemResource extends AbstractResource implements WritableResource {private final String path;@Nullableprivate final File file;private final Path filePath;public FileSystemResource(String path) {Assert.notNull(path, "Path must not be null");this.path = StringUtils.cleanPath(path);this.file = new File(path);this.filePath = this.file.toPath();}//other constructors......@Overridepublic InputStream getInputStream() throws IOException {try {return Files.newInputStream(this.filePath);}catch (NoSuchFileException ex) {throw new FileNotFoundException(ex.getMessage());}}...
}

可以看到它封装了java.io.File,在获取输入流时就是打开了该文件的NIO文件流。

ClassPathResource

ClassPathResource是类路径下资源的Resource实现。它通过ClassLoader或Class来加载资源。其构造函数和getInputStream()方法的实现如下:

public class ClassPathResource extends AbstractFileResolvingResource {private final String path;@Nullableprivate ClassLoader classLoader;@Nullableprivate Class<?> clazz;public ClassPathResource(String path, @Nullable ClassLoader classLoader) {Assert.notNull(path, "Path must not be null");String pathToUse = StringUtils.cleanPath(path);if (pathToUse.startsWith("/")) {pathToUse = pathToUse.substring(1);}this.path = pathToUse;this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());}//other constructors...public InputStream getInputStream() throws IOException {InputStream is;if (this.clazz != null) {is = this.clazz.getResourceAsStream(this.path);}else if (this.classLoader != null) {is = this.classLoader.getResourceAsStream(this.path);}else {is = ClassLoader.getSystemResourceAsStream(this.path);}if (is == null) {throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");}return is;}...}

可以看到它最终委托Class对象或ClassLoader对象来加载资源。如果创建该ClassPathResource时指定了用于加载资源的Class对象,它会通过该Class对象的getResourceAsStream来加载资源;否则,如果指定了ClassLoader,它会通过该ClassLoader对象的getResourceAsStream来加载资源;否则,通过系统ClassLoader来加载资源。

UrlResource

UrlResource是java.net.URL的Resource实现类,用来访问URL可以正常访问的任意对象。其内部封装了URL对象,因此,它支持http、https、file、ftp、jar等协议。其getInputStream()方法实现如下:

public InputStream getInputStream() throws IOException {URLConnection con = this.url.openConnection();ResourceUtils.useCachesIfNecessary(con);try {return con.getInputStream();}catch (IOException ex) {// Close the HTTP connection (if applicable).if (con instanceof HttpURLConnection) {((HttpURLConnection) con).disconnect();}throw ex;}
}

可以看到它是打开一个新的到该URL所引用的远程对象的连接,获取URLConnection对象,然后获取输入流。

InputStreamResource

resource注解的用法,InputStreamResource把一个InputStream封装为Resource,其内部实现大致如下:

public class InputStreamResource extends AbstractResource {private final InputStream inputStream;private final String description;private boolean read = false;public InputStreamResource(InputStream inputStream, @Nullable String description) {Assert.notNull(inputStream, "InputStream must not be null");this.inputStream = inputStream;this.description = (description != null ? description : "");}...@Overridepublic InputStream getInputStream() throws IOException, IllegalStateException {if (this.read) {throw new IllegalStateException("InputStream has already been read - " +"do not use InputStreamResource if a stream needs to be read multiple times");}this.read = true;return this.inputStream;}...
}

ByteArrayResource

ByteArrayResource把字节数组封装为Resource。其实现大致如下:

public class ByteArrayResource extends AbstractResource {private final byte[] byteArray;private final String description;public ByteArrayResource(byte[] byteArray, @Nullable String description) {Assert.notNull(byteArray, "Byte array must not be null");this.byteArray = byteArray;this.description = (description != null ? description : "");}...@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(this.byteArray);}...
}

ResourceLoader

接口定义

ResourceLoader的接口定义如下:

public interface ResourceLoader {Resource getResource(String location);@NullableClassLoader getClassLoader();
}

其中,getResource(String)接口返回指定位置的资源,getClassLoader()接口返回该ResourceLoader所使用的ClassLoader。

ResourceLoader接口的实现

ApplicationContext

Spring应用上下文都实现了ResourceLoader接口,因此所有的应用上下文都可以通过getResource(String)方法获取Resource实例。

DefaultResourceLoader

DefaultResourceLoader是默认的ResourceLoader实现,同时它也是AbstractApplicationContext类的基类。它的实现大致如下:

public class DefaultResourceLoader implements ResourceLoader {@Nullableprivate ClassLoader classLoader;private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);...public DefaultResourceLoader(@Nullable ClassLoader classLoader) {this.classLoader = classLoader;}...@Override@Nullablepublic ClassLoader getClassLoader() {return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());}public void addProtocolResolver(ProtocolResolver resolver) {Assert.notNull(resolver, "ProtocolResolver must not be null");this.protocolResolvers.add(resolver);}public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());}@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");for (ProtocolResolver protocolResolver : this.protocolResolvers) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}if (location.startsWith("/")) {return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {// Try to parse the location as a URL...URL url = new URL(location);return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));}catch (MalformedURLException ex) {// No URL -> resolve as resource path.return getResourceByPath(location);}}}...
}

import java.lang,可以看到,在创建DefaultResourceLoader的时候可以指定类加载器,如果没有指定,就会使用默认的类加载器。

同时,该类维护了一个ProtocolResolver集合,用来处理其他协议。ProtocolResolver是一个接口,其源码如下:

@FunctionalInterface
public interface ProtocolResolver {@NullableResource resolve(String location, ResourceLoader resourceLoader);}

这是一个函数接口,其唯一的抽象方法resolve,第一个参数是资源路径,第二个参数是针对该类协议的资源的资源加载器。

然后我们看一下DefaultResourceLoader的getResource(String)方法,该方法首先通过用户自己注册的ProtocolResolver尝试解析和加载资源,如果成功则直接返回;若不成功,则判断资源路径是否以/开头,如果以/开头,则直接返回一个ClassPathContextResource,若不是以/开头,但是以classpath:开头,则直接返回一个ClassPathResource,否则,根据协议返回FileUrlResource或UrlResource。

ResourcePatternResolver

ResourcePatternResolver是继承自ResourceLoader接口的一个接口,用来解析路径模式,例如Ant风格的路径模式,其源码如下:

public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String locationPattern) throws IOException;
}

其中,getResources(String)接口根据输入的路径模式,返回Resource数组。

spring源码rmi?ResourcePatternResolver的一个实现是PathMatchingResourcePatternResolver,它可以解析指定的资源位置路径到一个或多个匹配的Resource,这里,资源路径可以是一个指向某一个特定的资源的简单路径,也可以是包含classpath*:前缀的路径,路径里还可以使用Ant风格正则表达式。例如,你可以使用以下这些路径:

  • file:C:/context.xml;
  • classpath:/context.xml;
  • /WEB-INF/context.xml;
  • /WEB-INF/*-context.xml;
  • file:C:/some/path/*-context.xml;
  • com/mycompany/**&#47;applicationContext.xml
  • classpath:com/mycompany/**&#47;applicationContext.xml
  • classpath*:META-INF/*-beans.xml

Ant风格正则表达式的细节可以参见这篇文章:https://jinnianshilongnian.iteye.com/blog/1416322。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/101763.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息