# 2.7应用程序上下文和资源路径

本节介绍如何使用资源创建应用程序上下文，包括使用XML的快捷方式、如何使用通配符以及其他详细信息。

## 2.7.1 构造Application Contexts

application context constructor（特定context类型）通常使用string或者string数组作为resources路径的地址，比如定义context 的XML地址。

如果该地址没有前缀，那么该定义bean定义的Resource将会依赖于和他最相近的application context.比如下面的例子会使用ClassPathXmlApplicationContext：

```
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
```

这个bean定义将会从classpath加载，因为ClassPathResource被使用。但是下面的例子将会创建FileSystemXmlApplicationContext：

```
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/appContext.xml");
```

现在bean定义从文件系统加载。

如果加上前缀，那么将会覆盖默认的Resource类型。如下：

```
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
```

这里虽然使用了FileSystemXmlApplicationContext，但是还是会从classpath中加载。但是如果在后面的代码中，还有需要使用到ResourceLoader的， 那么没有前缀的paths仍然会被认为是filesystem路径。

**构造ClassPathXmlApplicationContext-快捷方式**

ClassPathXmlApplicationContext公开了许多构造函数，以方便实例化。基本思想是，你只需提供一个字符串数组，该数组只包含XML文件本身的文件名（不包含前导路径信息），还提供一个类。然后，ClassPathXmlApplicationContext从提供的类中派生路径信息。

考虑下面的文件结构：

```
com/
  foo/
    services.xml
    daos.xml
    MessengerService.class
```

下面的示例演示如何实例化由名为services.xml和daos.xml（位于类路径上）文件中定义的bean组成的ClassPathXmlApplicationContext实例：

```
ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "daos.xml"}, MessengerService.class);
```

## 2.7.2 应用程序上下文构造函数资源路径中的通配符

应用程序上下文构造函数值中的资源路径可以是简单路径（如前所示），每个路径都有到目标资源的一对一映射，或者可以包含特殊的“classpath\*：”前缀或内部ant样式正则表达式（通过使用Spring的PathMatcher实用程序）。后者都是有效的通配符。

此机制的一个用途是在需要进行组件样式的应用程序组装时使用。所有组件都可以将上下文定义片段“发布”到一个众所周知的位置路径，并且，当使用前缀为classpath\*：的同一路径创建最终应用程序上下文时，所有组件片段都会自动拾取。

注意，这个通配符是特定于应用程序上下文构造函数中资源路径的使用（或者直接使用PathMatcher实用程序类层次结构时），并且在构造时解析。它与资源类型本身无关。不能使用classpath\*：前缀构造实际资源，因为资源一次只指向一个资源。

**Ant-style Patterns**

下面是 Ant-style patterns 的路径：

```
/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等），则从该URL获取java.io.File，并通过遍历文件系统来解析通配符。对于JAR URL，解析器要么从中获取java.net.JarURLConnection，要么手动解析JAR URL，然后遍历JAR文件的内容以解析通配符。

**对可移植性的影响**

如果指定的路径已经是一个文件URL（或者隐式地因为ResourceLoader是一个文件系统，或者显式地指定），则通配符是完全可移植的。

如果指定的路径是类路径位置，解析程序必须通过调用ClassLoader.getResource（）获取最后一个非通配符路径段URL。因为这只是路径的一个节点（而不是文件的末尾），所以实际上（在类加载器JavaDoc中）没有定义在这种情况下返回的具体URL类型。实际上，它始终是一个java.io.File，表示目录（类路径资源解析为文件系统位置）或某种类型的JAR URL（类路径资源解析为JAR位置）。尽管如此，这个操作还是存在可移植性问题。

如果为最后一个非通配符段获取JAR URL，解析程序必须能够从中获取java.net.JarURLConnection，或者手动解析JAR URL，以便能够遍历JAR的内容并解析通配符。这在大多数环境中都有效，但在其他环境中失败，我们强烈建议在依赖JAR之前，在特定环境中彻底测试来自JAR的资源的通配符解析。

**classpath\*:前缀**

构造基于XML的application context，路径地址可以使用classpath\*: 前缀，如下：

```
ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
```

此特殊前缀指定必须获取与给定名称匹配的所有类路径资源（在内部，这基本上是通过调用ClassLoader.getResources（…）实现的），然后合并以形成最终的应用程序上下文定义。

> 通配符类路径依赖于基础类加载器的getResources（）方法。由于现在大多数应用程序服务器都提供自己的类加载器实现，因此其行为可能会有所不同，尤其是在处理JAR文件时。检查ClassPath\*是否有效的一个简单测试是使用ClassLoader从类路径上的jar中加载文件：getClass().getClassLoader().getResources("")。尝试使用具有相同名称但位于两个不同位置的文件进行此测试。如果返回不适当的结果，请检查应用程序服务器文档中可能影响类加载器行为的设置。

你还可以在位置路径的其余部分（例如，classpath\_:META-INF/\_-beans.xml）中将classpath\*：前缀与PathMatcher模式组合。在这种情况下，解析策略相当简单：在最后一个非通配符路径段上使用ClassLoader.getResources()调用以获取类加载程序层次结构中的所有匹配资源，然后在每个资源上，使用前面描述的相同路径匹配器解析策略通配符子路径。

**通配符的其他关注点**

注意，当与Ant样式模式结合使用时，除非实际目标文件位于文件系统中，否则classpath\_: 在模式开始之前只能在至少有一个根目录的情况下可靠的执行。这意味着classpath\_:\*.xml等模式可能不会从jar文件的根目录中检索文件，而是只从扩展目录的根目录中检索文件。

Spring检索类路径条目的能力源自JDK的 ClassLoader.getResources()）方法，该方法只返回空字符串的文件系统位置（指示要搜索的潜在根）。Spring也评估了JAR文件中的URLClassLoader运行时配置和java.class.path清单，但这并不保证会导致可移植行为。

> 扫描classpath包需要在classpath中存在相应的目录。当你使用Ant构建JAR时，不要激活JAR任务的“仅文件”开关。此外，根据某些环境中的安全策略，类路径目录可能不会被公开-例如，JDK 1.7.0 U45及更高版本上的独立应用程序（需要在清单中设置“可信库”）。请参阅[https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources）。](https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources%EF%BC%89%E3%80%82) 在JDK9的模块路径（Jigsaw）上，Spring的类路径扫描通常按预期工作。在这里，将资源放入一个专用的目录也是非常推荐的，这样可以避免前面提到的在搜索JAR文件根级别时的可移植性问题。

Ant-style 模式classpath: resources 并不一定能保证匹配的资源，如果要搜索的根package存在于多个类路径地址中。考虑下面的资源情况：

```
com/mycompany/package1/service-context.xml
```

下面是可能用到的Ant-style path 匹配：

```
classpath:com/mycompany/**/service-context.xml
```

这样的资源只能位于一个位置，但是当使用前面的示例这样的路径来尝试解析它时，解析程序将使用getResource（“com/mycompany”）；返回的（第一个）URL。如果此基本包节点存在于多个类加载器位置中，则实际的最终资源可能找不到。因此，在这种情况下，你应该更喜欢使用具有相同ant样式模式的classpath\*:来搜索包含根包的所有类路径位置。

## 2.7.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");
```
