最近在网上看到有人问 @ContextConfiguration("classpath:/bean.xml") 中除了classpath这种还有其他的写法么,看他的意思是想从本地文件系统中一个绝对目录加载配置文件,比如d:\\pathDir\\bean.xml这种。当时也试了这种本地的文件方式,但是把这个\\这个写成了//导致了不可以,于是武断的认为@ContextConfiguration("classpath:/bean.xml")这个不能使用别的方式。先说下结论,@ContextConfiguration("file:D:\\gateway-oauth2-pro\\bean.xml") 这种方式也是可以的。其实不然,这spring对资源的加载有一套独特的机制。
Resoure接口
spring对资源的加载离不开Resource这个强大的接口,它对具体的资源访问进行了抽象。借助idea我们可以看到如下
Resource接口继承了InputStreamSource这个接口,这个接口只有一个方法,getInputStream 用户获取输入流,这是资源读取的顶级接口,Resource在此之上扩展了exists,isReadable等方法,同时它拥有个抽象的AbstractResource实现,封装了一些公有资源的默认实现,另外,它拥有个子接口ContextResource 用于对web容器这一块的扩展。
Resoure具体实现
- UrlResource : 包装一个 java.net.URL ,可用于访问通常可以通过 URL 访问的任何对象,例如文件, HTTP 目标, FTP 目标等。所有 URL 都有一个标准化的 String 表示形式,因此适当的标准化前缀可用于指示另一种 URL 类型。如: file : 访问文件系统路径, http : 通过 HTTP 协议 ftp : 访问资源,通过 FTP 访问资源等
- ClassPathResource : 此类表示应从类路径获取的资源。它使用线程上下文类加载器( ClassLoader ),给定的类加载器或给定的类来加载资源
- FileSystemResource : 是一个 Resource 执行 java.io.File 和 java.nio.file.Path 类型资源的封装,它支持 File 和 URL , 实现 WritableResource 接口,且从 Spring Framework 5.0 开始, FileSystemResource 使用 NIO2 API 进行读/写交互
- ServletContextResource : 该 ServletContex t资源解释相关 Web 应用程序的根目录内的相对路径。
- InputStreamResource : 将给定的 InputStream 作为一种资源的 Resource 的实现类
- ByteArrayResource : 这是Resource给定字节数组的实现。它为给定的字节数组创建一个 ByteArrayInputStream
ResourceLoader接口
ResouceLoder接口拥有一个核心方法 Resource getResource(String location);用于获取Resouce实例。根据传入的location返回对应特有的Resource实例 。 其实所有的上下文都实现了这个接口,用于获取Resouce实例对象。有时候我们可以根据特定的上下文环境获取这个特殊的实例。
例如
classpath:classpath:com/myapp/config.xml从类路径加载
file:file:///data/config.xml从文件系统作为 URL 加载
http:https://myserver/logo.png按照URL形式加载
(none)/data/config.xml取决于应用上下文
ResourceLoader接口有个默认的实现类DefaultResourceLoader 它有两种构造函数,一个无参数,一个提供一个ClassLoader类型的构造函数,默认无参的构造方法里面其实ClassLoader使用的是
ClassUtils.getDefaultClassLoader();这个我们可以通过阅读源码得知。
接下来我们看DefaultResourceLoader 中getResource方法的实现,源码如下
首先时location参数的一个断言,接下来一个for循环 用于用户自定义资源协议的实现,这个其实使用的就是spi机制,不得不说spring考虑的还挺周到,时时不忘用户的个性化需求实现,这里暂时不谈。然后就是根据location做各种判断,这里其实有点策略模式的味道。这里面getResourceByPath方法的实现如下
protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); } 其中 ClassPathContextResource 就是前面ContextResource接口的孙子实现,中间还有个儿子ClassPathResource
这里回到本文开头的问题,@ContextConfiguration的location参数就是根据这个DefaultResourceLoader 的getResource方法来做到各种资源的加载适配。
这里还有俩个问题
1 如图 ProtocolResolver 是如何运用的
2 getResourceByPath方法 直接返回的就是ClassPathContextResource实例对象,未免实现太过于不完善,spring是怎么解决的。
本文暂时没有评论,来添加一个吧(●'◡'●)