7、作为依赖的资源
如果 bean 本身要通过某种动态过程确定和提供资源路径,那么 bean 使用 ResourceLoader 或 ResourcePatternResolver 接口加载资源可能是有意义的。 例如,考虑加载某种模板,其中所需的特定资源取决于用户的角色。 如果资源是静态的,完全消除使用 ResourceLoader 接口(或 ResourcePatternResolver 接口)是有意义的,让 bean 公开它需要的 Resource 属性,并期望它们被注入到其中。
核心框架 Spring Boot
SOA Spring Cloud
安全框架 Spring Security Oauth2
注册中心/配置中心 Nacos 集群部署
网关 Spring Cloud Gateway 部署多套,使用nginx负载
链路监控 Skywalking
流量控制、熔断降级 Sentinel
数据库 Mysql
分布式事务 Seata
定时调度 xxl-job
缓存 Redis
MQ RocketMq
持久框架 Mybatis Plus
搜索引擎 ElasticSearch
架构源码可以加我WX:445909108
然后注入这些属性变得微不足道的是,所有应用程序上下文都注册并使用特殊的 JavaBeans PropertyEditor,它可以将 String 路径转换为 Resource 对象。 例如,以下 MyBean 类具有类型为 Resource 的模板属性。
package example;
public class MyBean {
private Resource template;
public setTemplate(Resource template) {
this.template = template;
}
// ...
}
在 XML 配置文件中,可以使用该资源的简单字符串配置模板属性,如以下示例所示:
请注意,资源路径没有前缀。 因此,由于应用程序上下文本身将用作 ResourceLoader,因此根据应用程序上下文的确切类型,通过 ClassPathResource、FileSystemResource 或 ServletContextResource 加载资源。
如果需要强制使用特定的资源类型,可以使用前缀。 以下两个示例展示了如何强制 ClassPathResource 和 UrlResource(后者用于访问文件系统中的文件):
如果 MyBean 类被重构以用于注解驱动的配置,则 myTemplate.txt 的路径可以存储在名为 template.path 的键下——例如,在一个可供 Spring 环境使用的属性文件中(请参阅环境抽象)。 然后可以使用属性占位符通过 @Value 注解引用模板路径(请参阅使用@Value)。 Spring 会以字符串的形式检索模板路径的值,并且一个特殊的 PropertyEditor 会将字符串转换为一个 Resource 对象,以注入到 MyBean 构造函数中。 以下示例演示了如何实现这一点。
@Component
public class MyBean {
private final Resource template;
public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}
// ...
}
如果我们想支持在类路径中多个位置的同一路径下发现的多个模板 — 例如,在类路径中的多个 jar 中 — 我们可以使用特殊的 classpath*: 前缀和通配符来定义一个 templates.path 键作为 classpath* :/config/templates/*.txt。 如果我们如下重新定义 MyBean 类,Spring 会将模板路径模式转换为可以注入 MyBean 构造函数的 Resource 对象数组。
@Component
public class MyBean {
private final Resource[] templates;
public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}
// ...
}
8、应用上下文和资源路径
本节介绍如何使用资源创建应用程序上下文,包括使用 XML 的快捷方式、如何使用通配符以及其他详细信息。
8.1 构建应用上下文
应用程序上下文构造函数(针对特定的应用程序上下文类型)通常将字符串或字符串数组作为资源的位置路径,例如构成上下文定义的 XML 文件。
当这样的位置路径没有前缀时,从该路径构建并用于加载 bean 定义的特定资源类型取决于特定应用程序上下文并适合于特定应用程序上下文。 例如,考虑以下示例,它创建了一个 ClassPathXmlApplicationContext:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
bean 定义是从类路径加载的,因为使用了 ClassPathResource。 但是,请考虑以下示例,它创建了一个 FileSystemXmlApplicationContext:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
现在 bean 定义从文件系统位置加载(在这种情况下,相对于当前工作目录)。
请注意,在位置路径上使用特殊的类路径前缀或标准 URL 前缀会覆盖为加载 bean 定义而创建的默认资源类型。 考虑以下示例:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
使用 FileSystemXmlApplicationContext 从类路径加载 bean 定义。 但是,它仍然是一个 FileSystemXmlApplicationContext。 如果随后将其用作 ResourceLoader,则任何未带前缀的路径仍将被视为文件系统路径。
8.1.1 构造ClassPathXmlApplicationContext实例 — 快捷方式
ClassPathXmlApplicationContext 公开了许多构造函数以实现方便的实例化。 基本思想是,您可以仅提供一个字符串数组,该数组只包含 XML 文件本身的文件名(没有前导路径信息),并且还提供一个类。 ClassPathXmlApplicationContext 然后从提供的类派生路径信息。
考虑以下目录布局:
com/
example/
services.xml
repositories.xml
MessengerService.class
以下示例显示了如何实例化由名为 services.xml 和 repositories.xml(位于类路径)的文件中定义的 bean 组成的 ClassPathXmlApplicationContext 实例:
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
有关各种构造函数的详细信息,请参阅 ClassPathXmlApplicationContext javadoc。
8.2 应用程序上下文构造函数资源路径中的通配符
应用程序上下文构造函数值中的资源路径可能是简单路径(如前面所示),每个路径都具有到目标资源的一对一映射,或者可能包含特殊的 classpath*:前缀或内部 Ant 样式 模式(通过使用 Spring 的 PathMatcher 实用程序匹配)。 后者都是有效的通配符。
这种机制的一个用途是当您需要进行组件样式的应用程序组装时。 所有组件都可以将上下文定义片段发布到一个众所周知的位置路径,并且,当使用以 classpath*:为前缀的相同路径创建最终应用程序上下文时,所有组件片段都会被自动拾取。
请注意,此通配符特定于应用程序上下文构造函数中资源路径的使用(或当您直接使用 PathMatcher 实用程序类层次结构时),并在构造时解析。 它与资源类型本身无关。 您不能使用 classpath*: 前缀来构造实际的资源,因为资源一次只指向一个资源。
8.2.1 Ant 样式模式
路径位置可以包含 Ant 样式的模式,如以下示例所示:
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
当路径位置包含 Ant 样式模式时,解析器将遵循更复杂的过程来尝试解析通配符。 它为直到最后一个非通配符段的路径生成一个资源,并从中获取一个 URL。 如果此 URL 不是 jar: URL 或特定于容器的变体(例如 WebLogic 中的 zip:、WebSphere 中的 wsjar 等),则从中获取 java.io.File 并用于通过遍历 文件系统。 对于 jar URL,解析器要么从中获取 java.net.JarURLConnection,要么手动解析 jar URL,然后遍历 jar 文件的内容以解析通配符。
8.2.2 对可移植性的影响
如果指定的路径已经是文件 URL(隐式地因为基本 ResourceLoader 是一个文件系统,或者显式地),通配符保证以完全可移植的方式工作。
如果指定的路径是类路径位置,则解析器必须通过调用 Classloader.getResource()来获取最后一个非通配符路径段 URL。 由于这只是路径的一个节点(而不是末尾的文件),实际上(在 ClassLoader javadoc 中)在这种情况下返回的 URL 类型究竟是什么类型的。 实际上,它始终是表示目录(类路径资源解析为文件系统位置的位置)或某种类型的 jar URL(类路径资源解析为 jar 位置的位置)的java.io.File。 尽管如此,此操作仍存在可移植性问题。
如果最后一个非通配符段获得了 jar URL,则解析器必须能够从中获取 java.net.JarURLConnection 或手动解析 jar URL,以便能够遍历 jar 的内容并解析通配符 。这在大多数环境中都有效,但在其他环境中无效,我们强烈建议在依赖它之前,在您的特定环境中彻底测试来自 jar 的资源的通配符解析。
8.2.3 classpath*:前缀
在构建基于 XML 的应用程序上下文时,位置字符串可以使用特殊的 classpath*: 前缀,如以下示例所示:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
这个特殊的前缀指定必须获取与给定名称匹配的所有类路径资源(在内部,这基本上是通过调用ClassLoader.getResources(... ) 发生的),然后合并以形成最终的应用程序上下文定义。
通配符类路径依赖于底层 ClassLoader 的 getResources() 方法。 由于现在大多数应用程序服务器都提供自己的 ClassLoader 实现,因此行为可能会有所不同,尤其是在处理 jar 文件时。 检查 classpath* 是否有效的一个简单测试是使用 ClassLoader 从类路径上的 jar 中加载文件:getClass().getClassLoader().getResources("
您还可以在位置路径的其余部分(例如,classpath*:META-INF/*-beans.xml)中将 classpath*: 前缀与 PathMatcher 模式结合起来。 在这种情况下,解析策略相当简单:在最后一个非通配符路径段上使用 ClassLoader.getResources() 调用来获取类加载器层次结构中的所有匹配资源,然后从每个资源中获取相同的 PathMatcher 解析 前面描述的策略用于通配符子路径。
8.2.4 与通配符相关的其他说明
请注意,当与 Ant 风格的模式结合使用时,classpath*:只能在模式启动之前至少在一个根目录下可靠地工作,除非实际的目标文件驻留在文件系统中。 这意味着诸如 classpath*:*.xml之类的模式可能不会从 jar 文件的根目录检索文件,而只能从扩展目录的根目录检索文件。
Spring 检索类路径条目的能力源自 JDK 的 ClassLoader.getResources() 方法,该方法仅返回空字符串的文件系统位置(指示要搜索的潜在根)。 Spring 也会评估 URLClassLoader 运行时配置和 jar 文件中的 java.class.path 清单,但这并不能保证导致可移植行为。
类路径包的扫描需要在类路径中存在相应的目录条目。 使用 Ant 构建 JAR 时,不要激活 JAR 任务的仅文件开关。 此外,基于某些环境中的安全策略,类路径目录可能不会公开 — 例如,JDK 1.7.0_45 及更高版本上的独立应用程序(需要在清单中设置“Trusted-Library”。请参阅 https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。
在 JDK 9 的模块路径(Jigsaw)上,Spring 的类路径扫描通常按预期工作。 在这里也强烈建议将资源放入专用目录,以避免上述搜索 jar 文件根级别的可移植性问题。
带有类路径的 Ant 样式模式:如果要搜索的根包在多个类路径位置可用,则不能保证资源找到匹配的资源。 考虑以下资源位置示例:
com/mycompany/package1/service-context.xml
现在考虑某人可能用来尝试查找该文件的 Ant 样式路径:
classpath:com/mycompany/**/service-context.xml
这样的资源可能只存在于类路径中的一个位置,但是当使用前面示例之类的路径尝试解析它时,解析器会使用 getResource("com/mycompany");返回的(第一个)URL; . 如果此基础包节点存在于多个 ClassLoader 位置,则所需资源可能不存在于找到的第一个位置。 因此,在这种情况下,您应该更喜欢使用具有相同 Ant 样式模式的 classpath*:,它搜索包含 com.mycompany 基础包的所有类路径位置:classpath*:com/mycompany/**/service-context.xml。
8.3 FileSystemResource 注意事项
未附加到 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");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");