Spring - Resources
Java 标准所提供的访问资源的类(如 java.net.URL、java.io.File 等)并不能很好地满足各种底层资源的访问需求,比如缺少从类路径或者 Web 容器的上下文中获取资源的操作类。
Resource 接口
Spring 提供了一个 Resource 接口,它为应用提供了更强的底层资源访问能力。Resource 接口主要的几个方法:
- exists():返回一个 boolean 值,表示资源是否存在。
- isOpen():返回一个 boolean 值,表示资源是否打开。
- getInputStream():放资源对应的输入流。
- getURL():如果底层资源可以表示成 URL,则返回对应的 URL对象。
- getFile():如果底层资源对应一个文件,则返回对应的 File 对象。
- getDescription():返回资源的描述。当处理资源出错时,资源的描述会用于错误信息的输出。一般来说,资源的描述是一个完全限定的文件名称或当前资源的真实 URL。
内置的 Resource 实现
UrlResource
UrlResource 封装了 java.net.URL,它使用户能够访问任何可以通过 URL 表示的资源,如 HTTP 资源、FTP 资源、文件系统资源等。例:
try { Resource resource = new UrlResource("http://example.com/"); InputStream inStream = resource.getInputStream(); String content = IOUtils.toString(inStream); System.out.println(content); } catch (IOException e) { e.printStackTrace(); }
ClassPathResource
ClassPathResource 可以访问类路径下的资源,资源以相对于类路径的方式表示。例:
try { Resource resource = new ClassPathResource("conf/jdbc.properties"); InputStream inStream = resource.getInputStream(); String content = IOUtils.toString(inStream); System.out.println(content); } catch (IOException e) { e.printStackTrace(); }
FileSystemResource
FileSystemResource 可以访问文件系统资源,资源以文件系统路径的方式表示。例:
try { Resource resource = new FileSystemResource("D:/test.txt"); InputStream inStream = resource.getInputStream(); String content = IOUtils.toString(inStream); System.out.println(content); } catch (IOException e) { e.printStackTrace(); }
ServletContextResource
ServletContextResource 是为了访问 Web 容器上下文中的资源而提供的类,负责以相对于 Web 应用根目录的路径加载资源。它支持以流和 URL 的方式访问,在 WAR 解包的情况下,也可以通过 File 方式访问。该类还可以直接从 JAR 包中访问资源。例:
@WebServlet(name = "helloServlet", urlPatterns = { "/hello.png" }) public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Resource resource = new ServletContextResource(req.getServletContext(), "/image/1.png"); InputStream inStream = resource.getInputStream(); byte[] byteArray = IOUtils.toByteArray(inStream); resp.getOutputStream().write(byteArray); } }
InputStreamResource
InputStreamResource 可以访问以输入流返回表示的资源。
ByteArrayResource
ByteArrayResource 可以访问以二进制数值表示的资源。
PathResource
PathResource 是 Spring 4.x 提供的读取资源文件的新类。Path 封装了 java.net.URL、java.nio.file.Path、文件系统资源,它使用户能够访问任何可以通过 URL、Path、系统文件路径表示的资源。
ResourceLoader
为了访问不同类型的资源,必须使用相对应的 Resource 实现类,这个是比较麻烦的。是否可以在不显示使用 Resource 实现类的情况下,仅通过资源地址的特殊标识就可以访问相应的资源呢?Spring 提供了一个强大的加载资源的机制,不但能够通过 “classpath:”、“file:” 等资源地址前缀识别不同的资源类型,还支持 Ant 风格带通配符的资源地址。
资源地址表达式
地址前缀 | 对应的资源类型 | 示例 |
classpath: | 从类路径中加载资源,classpath: 和 classpath:/ 是等价的,都是相对于类的根路径。资源文件可以在标准的系统文件中,也可在 JAR 或 ZIP 的类包中。 | classpath:com/huey/hello/mapper/UserMapper.xml |
file: | 从文件系统目录中装载资源,可采用绝对或相对路径。 | file:/config/jdbc.properties |
http:// | 从 HTTP 服务器中装载资源。 | http://www.example.com/resource/beans.xml |
ftp:// | 从 FTP 服务器中装载资源。 | ftp://www.example.com/resource/beans.xml |
(none) | 根据 ApplicationContext 的具体实现采用对应类型的 Resource。 | com/huey/hello/config/beans.xml |
与 “classpath:” 对应的还有一种 “classpath*:” 前缀。假设有多个 JAR 包或文件系统类路径都拥有一个相同的包名(如 com.huey)。“classpath:” 只会在第一个加载的 com.huey 包的类路径下查找,而 “classpath*:” 会扫描所以这些 JAR 包及类路径下出现的 com.huey 类路径。这对于分模块打包的应用非常有用。假设一个名为 helloworld 的应用共分成 3 个模块,一个模块对应一个配置文件,分别是 module1.xml、module2.xml 及 module3.xml,都放到 spring/config 目录下,每个模块单独打成 JAR 包。使用 “classpath*:spring/config/module*.xml” 可以成功加载这 3 个配置文件,而使用 “classpath:spring/config/module*.xml” 只会加载一个模块的配置文件。
Ant 风格的资源地址支持 3 种通配符:
- ?:匹配文件名中的一个字符。
- *:匹配文件名中的任意字符。
- **:匹配任意层路径。
资源加载器
Spring 定义了一个资源加载的接口,并提供实现类。
ResourceLoader 接口仅有一个 getResource(String location) 方法,可以根据一个资源地址加载文件资源。不过,资源地址仅支持带资源类型前缀的表达式,不支持 Ant 更改的资源路径表达式。ResourcePatternResolver 扩展 ResourceLoader 接口,定义了一个新的接口方法 getResources(String locationPattern),该方法支持带资源类型前缀及 Ant 风格的资源路径表达式。PathMatchingResourcePatternResolver 是 Spring 提供的标准实现类。例:
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { Resource[] resources = resolver.getResources("classpath*:conf/spring/*-beans.xml"); for (Resource resource : resources) { System.out.println(resource.getDescription()); } } catch (IOException e) { e.printStackTrace(); }
ClassPathXmlApplicationContext & FileSystemXmlApplicationContext
Spring 里的所有应用上下文都实现了 ResourcePatternResolver 接口,因此,所有的应用上下文都可以通过 getResource()、getResources() 方法获取 Resource 实例。当通过 ClassPathXmlApplicationContext 实例调用 getResource 或 getResources 方法时,当没有显示指定资源前缀时,则将路径解析为类路径,同时返回的 Resource 实例是 ClasspathResource 类型;FileSystemXmlApplicationContext 则将路径解析为文件系统路径,返回的 Resource 实例是 FileSystemResource 类型。当以资源路径为参数构造 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 实例时也是一样的。