1、Spring的资源(Resource)
首先需要说明的是,Spring并没有让ApplicationContext直接继承Resource接口,就像ApplicationContext接口也没有直接继承Environment接口一样。这应该也不难理解,采用这种组合的方式会让我们的类更加的轻量,也起到了解耦的作用。ApplicationContext跟Resource相关的接口的继承关系如下
不管是ResourceLoader还是ResourcePatternResolver其实都是为了获取Resource对象,不过ResourcePatternResolver在ResourceLoader的基础上扩展了一个获取多个Resource的方法,我们在后文会介绍。
接口简介
Resouce接口继承了 InputStreamSource.
public interface InputStreamSource { // 每次调用都将返回一个当前资源对应的java.io. InputStream字节流 InputStream getInputStream() throws IOException; }
public interface Resource extends InputStreamSource { // 用于判断对应的资源是否真的存在 boolean exists(); // 用于判断对应资源的内容是否可读。需要注意的是当其结果为true的时候,其内容未必真的可读,但如果返回false,则其内容必定不可读 default boolean isReadable() { return exists(); } // 用于判断当前资源是否代表一个已打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄露。该方法主要针对于实现类InputStreamResource,实现类中只有它的返回结果为true,其他都为false。 default boolean isOpen() { return false; } // 当前资源是否是一个文件 default boolean isFile() { return false; } //当前资源对应的URL。如果当前资源不能解析为一个URL则会抛出异常 URL getURL() throws IOException; //当前资源对应的URI。如果当前资源不能解析为一个URI则会抛出异常 URI getURI() throws IOException; // 返回当前资源对应的File。如果当前资源不能以绝对路径解析为一个File则会抛出异常。 File getFile() throws IOException; // 返回一个ReadableByteChannel default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } // 返回资源的长度 long contentLength() throws IOException; // 最后修改时间 long lastModified() throws IOException; // 根据当前资源以及相对当前资源的路径创建一个新的资源,比如当前Resource代表文件资源“d:/abc/a.java”,则createRelative(“xyz.txt”)将返回表文件资源“d:/abc/xyz.txt” Resource createRelative(String relativePath) throws IOException; // 返回文件一个文件名称,通常来说会返回该资源路径的最后一段 @Nullable String getFilename(); // 返回描述信息 String getDescription(); }
UML类图
因为实现了Resource接口的类很多,并且一些类我们也不常用到或者很简单,所以上图中省略了一些不重要的分支,接下来我们就一个个分析。
抽象基类AbstractResource
实现了Resource接口,是大多数Resource的实现类的基类,提供了很多通用的方法。
比如exists方法会检查是否一个文件或者输入流能够被打开。isOpen永远返回false。”getURL()” 和”getFile()”方法会抛出异常。toString将会返回描述信息。
FileSystemResource
基于java的文件系统封装而成的一个资源对象。
AbstractFileResolvingResource
将URL解析成文件引用,既会处理协议为:“file“的URL,也会处理JBoss的”vfs“协议。然后相应的解析成对应的文件系统引用。
ByteArrayResource
根据一个给定的字节数组构建的一个资源。同时给出一个对应的输入流
BeanDefinitionResource
只是对BeanDefinition进行的一次描述性的封装
InputStreamResource
是针对于输入流封装的资源,它的构建需要一个输入流。 对于“getInputStream ”操作将直接返回该字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。
UrlResource
UrlResource代表URL资源,用于简化URL资源访问。
UrlResource一般支持如下资源访问:
-http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);
-ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”);
-file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”);
ClassPathResource
JDK获取资源有两种方式
1.使用Class对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。 参数既可以是当前class文件相对路径(以文件夹或文件开头),也可以是当前class文件的绝对路径(以“/”开头,相对于当前classpath根目录)
2.使用ClassLoader对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。参数只能是绝对路径,但不以“/”开头
ClassPathResource代表classpath路径的资源,将使用给定的Class或ClassLoader进行加载classpath资源。 “isOpen”永远返回false,表示可多次读取资源。
ServletContextResource
是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和getResourceAsStream()方法来获取资源的。
ResourceLoader
接口简介
ResourceLoader接口被设计用来从指定的位置加载一个Resource,其接口定义如下
public interface ResourceLoader { // classpath: String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; // 核心方法,从指定位置加载一个Resource // 1.支持权限的的URL格式,如:file:C:/test.dat // 2.支持classpath的格式,如:classpath:test.dat // 3.支持文件相对路径,如:WEB-INF/test.dat Resource getResource(String location); // 返回用于加载该资源的ClassLoader @Nullable ClassLoader getClassLoader(); }
UML类图
对于一些不是很必要的类我都省略了,其实核心的类我们只需要关注DefaultResourceLoader就可以了,因为其余子类(除了GenericApplicationContext)都是直接继承了DefaultResourceLoader的getResource方法。代码如下:
@Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); // 正常来说protocolResolvers集合是空的,除非我们调用了它的addProtocolResolver方法添加了自定义协议处理器,调用addProtocolResolver方法所添加的协议处理器会覆盖原有的处理逻辑 for (ProtocolResolver protocolResolver : this.protocolResolvers) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } // 如果是以“/”开头,直接返回一个classpathResource if (location.startsWith("/")) { return getResourceByPath(location); } // 如果是形如:classpath:test.dat也直接返回一个ClassPathResource else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 否则将其解析为一个URL URL url = new URL(location); // 如果是一个文件,直接返回一个FileUrlResource,否则返回一个普通的UrlResource return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // 如果URL转换失败,还是作为一个普通的ClassPathResource return getResourceByPath(location); } } }
资源路径
ant-style
类似下面这种含有通配符的路径
/WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml