一、抛砖引玉

IOC容器的初始化,第一个过程就是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一的接口。简单来讲:资源的定义多种多样,由网络形式的资源、二进制形式存在的资源、以文件形式存在的资源、以字节流形式存在的资源等等,它们存在于任何场所,比如网络中、文件系统、应用程序中。所以Spring实现了自己的资源加载策略。这个过程类似于容器寻找数据的过程,就像用水桶装水就先要把水找到一样。




如何标识spring的resource文件夹_通过不同的访问路径加载不同的样式


二、统一资源:Resource

Resource 接口是 Spring 资源访问策略的抽象,其继承InputStreamSource。

public interface Resource extends InputStreamSource {}

它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。作为所有资源的统一抽象,Source 定义了一些通用的方法,由子类 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 */ 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();}● exists:返回Resource所指向的底层资源是否存在 ● isReadable:返回当前Resource代表的底层资源是否可读 ● isOpen:返回Resource资源文件是否已经打开,如果返回true,则只能被读取一次然后关闭以避免内存泄漏;常见的Resource实现一般返回false ● getURL:如果当前Resource代表的底层资源能由java.util.URL代表,则返回该URL,否则抛出IO异常 ● getURI:如果当前Resource代表的底层资源能由java.util.URI代表,则返回该URI,否则抛出IO异常 ● getFile:如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IO异常 ● contentLength:返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度。 ● lastModified:返回当前Resource代表的底层资源的最后修改时间。 ● createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源。 ● getFilename:返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径。 ● getDescription:返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)。

类结构图如下:


如何标识spring的resource文件夹_ide_02


从上图可以看到,Spring 为Resource 根据资源的不同类型提供不同的具体实现,如下:

  • UrlResource:访问网络资源的实现类。是对java.net.URL的包装。在java中,将不同来源的资源抽象成URL,通过注册不同的handler来处理不同来源的资源的读取逻辑。一般不同类型使用不同的前缀。
  • isOpen永远返回false,表示可多次读取资源。
  • UrlResource应该提供标准的协议前缀,一般支持如下资源访问:
  • http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);
  • ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”);
  • file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”)
UrlResource ur = new UrlResource("file:test.xml")
  • ClassPathResource:访问类加载路径里资源的实现类。
  • 代表classpath路径的资源,将使用ClassLoader进行加载资源。主要优势是方便访问类加载路径下的资源,尤其是Web应用,因为它可以自动搜索位于WEB-INF/classes下的资源文件
  • classpath资源存在于类路径中的文件系统中或jar包里,且isOpen永远返回false,表示可多次读取资源。
  • ClassPathResource加载资源替代了Class类和ClassLoader类的getResource(String name)和getResourceAsStream(String name)两个加载类路径资源方法,提供一致的访问方式。
  • ClassPathResource提供了三个构造器:
  • public ClassPathResource(String path):使用默认的ClassLoader加载“path”类路径资源;
  • public ClassPathResource(String path, ClassLoader classLoader):使用指定的ClassLoader加载“path”类路径资源;
  • public ClassPathResource(String path, Class> clazz):使用指定的类加载“path”类路径资源,将加载相对于当前类的路径的资源;
public static void test_ClassPathResource() throws IOException { Resource resource = new ClassPathResource("test.xml"); }
  • FileSystemResource:访问文件系统里资源的实现类。
  • InputStreamResource:访问输入流资源的实现类。
  • ByteArrayResource:访问字节数组资源的实现类。

这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

AbstractResource 为 Resource 接口的默认实现,它实现了 Resource 接口的大部分的公共实现,其定义如下:

public abstract class AbstractResource implements Resource { /** * 判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流 */ @Override public boolean exists() { try { return getFile().exists(); } catch (IOException ex) { // Fall back to stream existence: can we open the stream? try { InputStream is = getInputStream(); is.close(); return true; } catch (Throwable isEx) { return false; } } } /** * 直接返回true,表示可读 */ @Override public boolean isReadable() { return true; } /** * 直接返回 false,表示未被打开 */ @Override public boolean isOpen() { return false; } /** * 直接返回false,表示不为 File */ @Override public boolean isFile() { return false; } /** * 抛出 FileNotFoundException 异常,交给子类实现 */ @Override public URL getURL() throws IOException { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL"); } /** * 基于 getURL() 返回的 URL 构建 URI */ @Override public URI getURI() throws IOException { URL url = getURL(); try { return ResourceUtils.toURI(url); } catch (URISyntaxException ex) { throw new NestedIOException("Invalid URI [" + url + "]