java.net.URL
可以处理各种复杂的URL(统一资源定位),缺点是对于所有访问底层资源的能力还不够
例如还没有一种URL的实现可以访问需要从类路径或相对于ServletContext获得的资源虽然可以为专门的URL前缀注册新的处理程序(类似于现有的用于前缀的处理程序,如http:),但这通常非常复杂。而且URL接口仍然缺少一些需要的功能,比如检查被指向的资源是否存在的方法
思考:Resource接口是用来解决什么问题的?即URL接口的这些不足。
比URL接口更强,用于抽象对底层资源的访问。
对比URL接口,Resource接口有何不同,URL表示一个统一的资源定位符,一个指向万维网上“资源”的指针,相较于Resource更加侧重于web资源,且无法判断资源是否存在
相较于URL Resource接口实现资源的抽象,可以检查资源是否存在。
URL
protocol,host,port,authority,file,ref
判断 | 提供资源 | 其他 |
---|---|---|
isReadable | URL | createRelative |
isOpen | URI | contentLength |
isFile | File | lastModified |
exists | InputStream | ReadableByteChannel |
通过字符串路径上的特殊前缀,指定必须创建和使用特定的资源实现,例如:ftp,http,file
资源抽象不能代替功能。在可能的地方进行包装。例如,UrlResource包装一个URL并使用包装后的URL来完成它的工作。
Resource接口的子类有以下这些
UrlResource
ClassPathResource
FileSystemResource
ServletContextResource
InputStreamResource
ByteArrayResource
包装了URL接口,可以用来访问File,Http,ftp等其他对象,UrlResource是由Java代码显式地使用UrlResource构造函数创建的,但通常在调用API方法时隐式地创建,该方法接受一个表示路径的字符串参数。对于后一种情况,JavaBeans PropertyEditor最终决定创建哪种类型的资源。路径字符串包含众所周知的(也就是)前缀(例如classpath:),它为该前缀创建一个适当的专门化资源。但是,如果它不识别前缀,则假定该字符串是标准URL字符串,并创建一个UrlResource。
PropertyEditor
从类路径下获取(classpath),隐式创建当字符串路径上的特殊前缀classpath:,PropertyEditor将创建一个ClassPathResource实例
File和Path 的Resource实现,支持解析File和URL
InputStream的Resource实现,只有在没有特定的资源实现可用时,才应该使用它。特别是,尽可能选择ByteArrayResource或任何基于文件的资源实现。如果您需要将资源描述符保存在某个地方,或者需要多次读取流,请不要使用它
这是一个给定字节数组的资源实现。它为给定的字节数组创建一个ByteArrayInputStream。它对于从任何给定的字节数组加载内容都很有用,而不必求助于一次性使用的InputStreamResource
classpath: | ClassPathResource |
---|---|
File,Path | FileSystemResource |
InputStreamResource | 尽量不用 |
ByteArrayResource | byte array --ByteArrayInputStream |
由可以返回(即加载)资源实例的对象来实现的。
public interface ResourceLoader {
Resource getResource(String location);
}
applicationcontext于resouce的关系,如果没有指定特殊前缀,将获得适合于特定应用程序上下文的资源类型
上下文类型 | 返回资源类型 |
---|---|
ClassPathXmlApplicationContext | ClassPathResource |
FileSystemXmlApplicationContext | FileSystemResource |
WebApplicationContext | ServletContextResource |
用例
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
资源和字符串路径的关系
Prefix | Example | Explanation |
---|---|---|
classpath: | classpath:com/myapp/config.xml |
Loaded from the classpath. |
file: | file:///data/config.xml |
Loaded as a URL from the filesystem. See also FileSystemResource Caveats. |
http: | https://myserver/logo.png |
Loaded as a URL . |
(none) | /data/config.xml |
Depends on the underlying ApplicationContext . |
当类实现ResourceLoaderAware并部署到应用程序上下文中(作为Spring托管的bean)时,该类被应用程序上下文识别为ResourceLoaderAware。 然后,应用程序上下文调用setResourceLoader(ResourceLoader),将自身作为参数提供(请记住,Spring中的所有应用程序上下文均实现ResourceLoader接口)
在应用程序组件中,您还可以依赖自动装配ResourceLoader来实现ResourceLoaderAware接口。 “传统”构造函数和byType自动装配模式(如“自动装配协作器”中所述)能够分别为构造函数参数或setter方法参数提供ResourceLoader。 为了获得更大的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用基于注释的自动装配功能。 在这种情况下,只要有问题的字段,构造函数或方法带有@Autowired批注,ResourceLoader就会自动连接到需要ResourceLoader类型的字段,构造函数参数或方法参数中。 有关更多信息,请参见使用@Autowired。
即
ResourceLoaderAware
接口可以获得实现了ResourceLoader的类,好像还与自动装配存在着某种联系。自动装配的核心是BeanpostProcessor,其可以在bean实例化前后对bean进行一系列操作。
资源可以作为依赖
<bean id="myBean" class="...">
<property name="template" value="some/resource/path/myTemplate.txt"/>
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
bean>
应用上下文和资源路径
创建应用上下文需要依赖资源路径,应用上下文从资源路径中加载bean的定义,当这样的位置路径没有前缀时,从该路径构建并用于加载bean定义的特定资源类型依赖于特定的应用程序上下文,并且适合于特定的应用程序上下文。
应用上下文类型 | 资源路径 |
---|---|
new ClassPathXmlApplicationContext(“conf/appContext.xml”); | classpath |
new FileSystemXmlApplicationContext(“conf/appContext.xml”); | file system |
new FileSystemXmlApplicationContext(“classpath:conf/appContext.xml”); | classpath |
使用FileSystemXmlApplicationContext从类路径加载bean定义。但是,它仍然是FileSystemXmlApplicationContext。如果它随后被用作ResourceLoader,那么任何不带前缀的路径仍然被视为文件系统路径。
com/
foo/
services.xml
daos.xml
MessengerService.class
以下示例显示如何实例化由在名为service.xml和daos.xml(位于类路径中)的文件中定义的bean组成的ClassPathXmlApplicationContext实例。
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "daos.xml"}, MessengerService.class);
感觉对于classpath 和包名之间的关系还是有点不清晰
应用程序上下文构造器资源路径中的通配符
此机制的一个用途是,当您需要进行组件样式的应用程序组装时。所有组件都可以将上下文定义片段“发布”到一个已知的位置路径,并且,当使用以classpath*:作为前缀的相同路径创建最终的应用程序上下文时,所有组件片段都会自动被获取。注意,这种通配符是特定于在应用程序上下文构造函数中使用资源路径的,在构建应用时进行解析。
与资源类型本身无关。您不能使用classpath*:前缀来构造实际的资源,因为一个资源一次只能指向一个资源。
多模块应用中的使用classpath*
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
When the path location contains an Ant-style pattern, the resolver follows a more complex procedure to try to resolve the wildcard. It produces a Resource
for the path up to the last non-wildcard segment and obtains a URL from it. If this URL is not a jar:
URL or container-specific variant (such as zip:
in WebLogic, wsjar
in WebSphere, and so on), a java.io.File
is obtained from it and used to resolve the wildcard by traversing the filesystem. In the case of a jar URL, the resolver either gets a java.net.JarURLConnection
from it or manually parses the jar URL and then traverses the contents of the jar file to resolve the wildcards.
如果有路径机制含有 Ant-style pattern,解析器会使用更加复杂的程序尝试解析通配符。
匹配规则
The mapping matches URLs using the following rules:
Some examples:
如果指定的路径已经是一个文件URL(由于基本ResourceLoader是一个文件系统,所以它是隐式的,或者是显式的),则保证通配符可以完全可移植的方式工作。
如果指定的路径是类路径位置,则解析器必须通过调用Classloader.getResource()获得最后的非通配符路径段URL。 由于这只是路径的一个节点(而不是末尾的文件),因此实际上(在ClassLoader javadoc中)未定义确切返回的是哪种URL。 实际上,它始终是一个java.io.File,代表目录(类路径资源解析为文件系统位置)或某个jar URL(类路径资源解析为jar位置)。 尽管如此,此操作仍存在可移植性问题。
如果为最后一个非通配符段获取了jar URL,则解析程序必须能够从中获取java.net.JarURLConnection或手动解析jar URL,以便能够遍历jar的内容并解析通配符 。 这在大多数环境中确实有效,但在其他环境中则无效,因此我们强烈建议您在依赖特定环境之前,对来自jars的资源的通配符解析进行彻底测试
移植性于路径关系
路径样式 | 移植性是否有影响 |
---|---|
file URL | 无影响 |
classpath location | 可能有一些 |
jar URL | 彻底测试 |
classpath*:
Prefix在构造基于xml的应用程序上下文时,位置字符串可以使用特殊的classpath*:前缀,如下面的示例所示
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
这个特殊的前缀指定必须获取所有与给定名称匹配的类路径资源(在内部,这实际上是通过调用ClassLoader来实现的。然后合并形成最终的应用程序上下文定义
通配符类路径依赖于基础类加载器的getResources()方法。 由于当今大多数应用程序服务器都提供自己的类加载器实现,因此行为可能有所不同,尤其是在处理jar文件时。 检查classpath *是否可行的简单测试是使用classloader从classpath的jar中加载文件:getClass()。getClassLoader()。getResources(“ ”)。 尝试对具有相同名称但位于两个不同位置的文件进行此测试。 如果返回了不合适的结果,请检查应用程序服务器文档中可能影响类加载器行为的设置。
还可以在其余位置路径中将classpath *:前缀与PathMatcher模式结合使用(例如,classpath *:META-INF / *-beans.xml)。 在这种情况下,解析策略非常简单:在最后一个非通配符路径段上使用ClassLoader.getResources()调用,以获取类加载器层次结构中的所有匹配资源,然后从每个资源获取相同的PathMatcher解析 前面描述的策略用于通配符子路径。
注意classpath*:
,当与 Ant-style模式结合使用时,除非实际的目标文件驻留在文件系统中,否则classpath*:只能在模式启动之前可靠地与至少一个根目录一起工作。这意味着,类似classpath*.xml这样的模式可能不会从jar文件的根目录检索文件,而是只从扩展目录的根目录检索文件。
带有类路径的ant样式模式:如果要搜索的根包在多个类路径位置可用,则不能保证资源能够找到匹配的资源。考虑下面的资源位置示例
com/mycompany/package1/service-context.xml
现在考虑某人可能用来尝试找到该文件的Ant样式路径
classpath:com/mycompany/**/service-context.xml
这样的资源可能只在一个位置,但是当使用诸如上一示例的路径尝试对其进行解析时,解析器将根据getResource(“ com / mycompany”)返回的(第一个)URL进行工作。如果此基本包节点存在于多个类加载器位置,则实际的最终资源可能不存在。因此,在这种情况下,您应该使用与ant样式相同的classpath*:
,它搜索包含根包的所有类路径位置。
文件系统资源警告
未附加到FileSystemApplicationContext的FileSystemResource(即,当FileSystemApplicationContext不是实际的ResourceLoader时)将按您期望的那样处理绝对路径和相对路径。 相对路径是相对于当前工作目录的,而绝对路径是相对于文件系统的根的。
但是,出于向后兼容性(历史原因)的原因,当FileSystemApplicationContext是ResourceLoader时,这种情况会发生变化。FileSystemApplicationContext强制所有附加的FileSystemResource实例将所有位置路径视为相对路径,不管它们是否以正斜杠开头。在实践中,这意味着下面的例子是等价的
ApplicationContext ctx = **new** FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
下面的例子也是等价的(尽管它们不同是有意义的,因为一种情况是相对的,另一种情况是绝对的)
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
在实践中,如果您需要真正的绝对文件系统路径,您应该避免将绝对路径与FileSystemResource或FileSystemXmlApplicationContext一起使用,并通过使用file: URL前缀强制使用UrlResource。下面的例子演示了如何做到这一点
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");
参考文档