`

()Spring中资源的加载ResourceLoader

阅读更多

http://blog.csdn.net/u011955252/article/details/52912571

4.1.1  概述

       在日常程序开发中,处理外部资源是很繁琐的事情,我们可能需要处理URL资源、File资源资源、ClassPath相关资源、服务器相关资源(JBoss AS 5.x上的VFS资源)等等很多资源。因此处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性;而且处理这些资源步骤都是类似的(打开资源、读取资源、关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便,而且使我们系统更加简洁,都是对不同的底层资源使用同一个接口进行访问。

       spring 提供一个Resource接口来统一这些底层资源一致的访问,而且提供了一些便利的接口,从而能提供我们的生产力。

4.1.2  Resource接口

       spring的Resource接口代表底层外部资源,提供了对底层外部资源的一致性访问接口。

 

java代码:
Java代码  收藏代码
  1. public interface InputStreamSource {  
  2.     InputStream getInputStream() throws IOException;  
  3. }  

 

java代码:
Java代码  收藏代码
  1. public interface Resource extends InputStreamSource {  
  2.        boolean exists();  
  3.        boolean isReadable();  
  4.        boolean isOpen();  
  5.        URL getURL() throws IOException;  
  6.        URI getURI() throws IOException;  
  7.        File getFile() throws IOException;  
  8.        long contentLength() throws IOException;  
  9.        long lastModified() throws IOException;  
  10.        Resource createRelative(String relativePath) throws IOException;  
  11.        String getFilename();  
  12.        String getDescription();  
  13. }  

 

 

1)InputStreamSource接口解析:

         getInputStream:每次调用都将返回一个新鲜的资源对应的Java.io. InputStream字节流,调用者在使用完毕后必须关闭该资源。

2)Resource接口继承InputStreamSource接口,并提供一些便利方法:

         exists:返回当前Resource代表的底层资源是否存在,true表示存在。

         isReadable:返回当前Resource代表的底层资源是否可读,true表示可读。

         isOpen:返回当前Resource代表的底层资源是否已经打开,如果返回true,则只能被读取一次然后关闭以避免资源泄露;常见的Resource实现一般返回false。

         getURL:如果当前Resource代表的底层资源能由Java.util.URL代表,则返回该URL,否则抛出IOException。

         getURI:如果当前Resource代表的底层资源能由java.util.URI代表,则返回该URI,否则抛出IOException。

         getFile:如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException。

         contentLength:返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度。

         lastModified:返回当前Resource代表的底层资源的最后修改时间。

         createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源。

         getFilename:返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径。

         getDescription:返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)。

 

Resource接口提供了足够的抽象,足够满足我们日常使用。而且提供了很多内置Resource实现:ByteArrayResource、InputStreamResource 、FileSystemResource 、UrlResource 、ClassPathResource、ServletContextResource、VfsResource等。

 

 

4.2  内置Resource实现

4.2.1  ByteArrayResource

 

       ByteArrayResource代表byte[]数组资源,对于“getInputStream”操作将返回一个ByteArrayInputStream。

首先让我们看下使用ByteArrayResource如何处理byte数组资源:

 

java代码:
Java代码  收藏代码
  1.       
  2. package cn.javass.spring.chapter4;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import org.junit.Test;  
  6. import org.springframework.core.io.ByteArrayResource;  
  7. import org.springframework.core.io.Resource;  
  8. public class ResourceTest {  
  9. @Test  
  10. public void testByteArrayResource() {  
  11. Resource resource = new ByteArrayResource("Hello World!".getBytes());  
  12.         if(resource.exists()) {  
  13.             dumpStream(resource);  
  14.         }  
  15. }  
  16. }  

 

是不是很简单,让我们看下“dumpStream”实现:

 

java代码:
Java代码  收藏代码
  1. private void dumpStream(Resource resource) {  
  2.         InputStream is = null;  
  3.         try {  
  4.             //1.获取文件资源  
  5.             is = resource.getInputStream();  
  6.             //2.读取资源  
  7.             byte[] descBytes = new byte[is.available()];  
  8.             is.read(descBytes);  
  9.             System.out.println(new String(descBytes));  
  10.         } catch (IOException e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.         finally {  
  14.             try {  
  15.                 //3.关闭资源  
  16.                 is.close();  
  17.             } catch (IOException e) {  
  18.             }  
  19.         }  
  20.     }  

 

    让我们来仔细看一下代码,dumpStream方法很抽象定义了访问流的三部曲:打开资源、读取资源、关闭资源,所以dunpStrean可以再进行抽象从而能在自己项目中使用;byteArrayResourceTest测试方法,也定义了基本步骤:定义资源、验证资源存在、访问资源。

 

       ByteArrayResource可多次读取数组资源,即isOpen ()永远返回false。

 

1.2.2  InputStreamResource

       InputStreamResource代表java.io.InputStream字节流,对于“getInputStream ”操作将直接返回该字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。

       让我们看下测试代码吧:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testInputStreamResource() {  
  3.    ByteArrayInputStream bis = new ByteArrayInputStream("Hello World!".getBytes());  
  4.    Resource resource = new InputStreamResource(bis);  
  5.     if(resource.exists()) {  
  6.        dumpStream(resource);  
  7.     }  
  8.     Assert.assertEquals(true, resource.isOpen());  
  9. }  
  10.      

 

       测试代码几乎和ByteArrayResource测试完全一样,注意“isOpen”此处用于返回true。

 

4.2.3  FileSystemResource

       FileSystemResource代表java.io.File资源,对于“getInputStream ”操作将返回底层文件的字节流,“isOpen”将永远返回false,从而表示可多次读取底层文件的字节流。

       让我们看下测试代码吧:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testFileResource() {  
  3. File file = new File("d:/test.txt");  
  4.     Resource resource = new FileSystemResource(file);  
  5.     if(resource.exists()) {  
  6.         dumpStream(resource);  
  7.     }  
  8.     Assert.assertEquals(false, resource.isOpen());  
  9. }  
  10.    

 

       注意由于“isOpen”将永远返回false,所以可以多次调用dumpStream(resource)。

 

4.2.4  ClassPathResource

       ClassPathResource代表classpath路径的资源,将使用ClassLoader进行加载资源。classpath 资源存在于类路径中的文件系统中或jar包里,且“isOpen”永远返回false,表示可多次读取资源。

       ClassPathResource加载资源替代了Class类和ClassLoader类的“getResource(String name)”和“getResourceAsStream(String name)”两个加载类路径资源方法,提供一致的访问方式。

ClassPathResource提供了三个构造器:

         public ClassPathResource(String path):使用默认的ClassLoader加载“path”类路径资源;

         public ClassPathResource(String path, ClassLoader classLoader):使用指定的ClassLoader加载“path”类路径资源;

比如当前类路径是“cn.javass.spring.chapter4.ResourceTest”,而需要加载的资源路径是“cn/javass/spring/chapter4/test1.properties”,则将加载的资源在“cn/javass/spring/chapter4/test1.properties”;

         public ClassPathResource(String path, Class<?> clazz):使用指定的类加载“path”类路径资源,将加载相对于当前类的路径的资源;

比如当前类路径是“cn.javass.spring.chapter4.ResourceTest”,而需要加载的资源路径是“cn/javass/spring/chapter4/test1.properties”,则将加载的资源在“cn/javass/spring/chapter4/cn/javass/spring/chapter4/test1.properties”;

       而如果需要 加载的资源路径为“test1.properties”,将加载的资源为“cn/javass/spring/chapter4/test1.properties”。

 

       让我们直接看测试代码吧:

1)使用默认的加载器加载资源,将加载当前ClassLoader类路径上相对于根路径的资源:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testClasspathResourceByDefaultClassLoader() throws IOException {  
  3.    Resource resource = new ClassPathResource("cn/javass/spring/chapter4/test1.properties");  
  4.     if(resource.exists()) {  
  5.         dumpStream(resource);  
  6.     }  
  7.     System.out.println("path:" + resource.getFile().getAbsolutePath());  
  8.     Assert.assertEquals(false, resource.isOpen());  
  9. }  
  10.    

 

2)使用指定的ClassLoader进行加载资源,将加载指定的ClassLoader类路径上相对于根路径的资源:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testClasspathResourceByClassLoader() throws IOException {  
  3.     ClassLoader cl = this.getClass().getClassLoader();  
  4.     Resource resource = new ClassPathResource("cn/javass/spring/chapter4/test1.properties" , cl);  
  5.     if(resource.exists()) {  
  6.         dumpStream(resource);  
  7.     }  
  8.     System.out.println("path:" + resource.getFile().getAbsolutePath());  
  9.     Assert.assertEquals(false, resource.isOpen());  
  10. }  

 

3)使用指定的类进行加载资源,将尝试加载相对于当前类的路径的资源:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testClasspathResourceByClass() throws IOException {  
  3.    Class clazz = this.getClass();  
  4.     Resource resource1 = new ClassPathResource("cn/javass/spring/chapter4/test1.properties" , clazz);  
  5.     if(resource1.exists()) {  
  6.         dumpStream(resource1);  
  7.     }  
  8.     System.out.println("path:" + resource1.getFile().getAbsolutePath());  
  9.     Assert.assertEquals(false, resource1.isOpen());  
  10.          
  11.     Resource resource2 = new ClassPathResource("test1.properties" , this.getClass());  
  12.     if(resource2.exists()) {  
  13.         dumpStream(resource2);  
  14.    }  
  15.     System.out.println("path:" + resource2.getFile().getAbsolutePath());  
  16.     Assert.assertEquals(false, resource2.isOpen());  
  17. }  

       “resource1”将加载cn/javass/spring/chapter4/cn/javass/spring/chapter4/test1.properties资源;“resource2”将加载“cn/javass/spring/chapter4/test1.properties”;

 

4)加载jar包里的资源,首先在当前类路径下找不到,最后才到Jar包里找,而且在第一个Jar包里找到的将被返回:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void classpathResourceTestFromJar() throws IOException {  
  3. Resource resource = new ClassPathResource("overview.html");  
  4.     if(resource.exists()) {  
  5.         dumpStream(resource);  
  6.     }  
  7.     System.out.println("path:" + resource.getURL().getPath());  
  8.     Assert.assertEquals(false, resource.isOpen());  
  9. }  

 

如果当前类路径包含“overview.html”,在项目的“resources”目录下,将加载该资源,否则将加载Jar包里的“overview.html”,而且不能使用“resource.getFile()”,应该使用“resource.getURL()”,因为资源不存在于文件系统而是存在于jar包里,URL类似于“file:/C:/.../***.jar!/overview.html”。

类路径一般都是相对路径,即相对于类路径或相对于当前类的路径,因此如果使用“/test1.properties”带前缀“/”的路径,将自动删除“/”得到“test1.properties”。

 

4.2.5  UrlResource

       UrlResource代表URL资源,用于简化URL资源访问。“isOpen”永远返回false,表示可多次读取资源。

       UrlResource一般支持如下资源访问:

         http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);

         ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”);

         file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”);

具体使用方法在此就不演示了,可以参考cn.javass.spring.chapter4.ResourceTest中urlResourceTest测试方法。

 

4.2.6  ServletContextResource

       ServletContextResource代表web应用资源,用于简化servlet容器的ServletContext接口的getResource操作和getResourceAsStream操作;在此就不具体演示了。

 

4.2.7  VfsResource

VfsResource代表Jboss 虚拟文件系统资源。

 

Jboss VFS(Virtual File System)框架是一个文件系统资源访问的抽象层,它能一致的访问物理文件系统、jar资源、zip资源、war资源等,VFS能把这些资源一致的映射到一个目录上,访问它们就像访问物理文件资源一样,而其实这些资源不存在于物理文件系统。

在示例之前需要准备一些jar包,在此我们使用的是Jboss VFS3版本,可以下载最新的Jboss AS 6x,拷贝lib目录下的“jboss-logging.jar”和“jboss-vfs.jar”两个jar包拷贝到我们项目的lib目录中并添加到“Java Build Path”中的“Libaries”中。

让我们看下示例(cn.javass.spring.chapter4.ResourceTest):

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testVfsResourceForRealFileSystem() throws IOException {  
  3. //1.创建一个虚拟的文件目录  
  4. VirtualFile home = VFS.getChild("/home");  
  5. //2.将虚拟目录映射到物理的目录  
  6. VFS.mount(home, new RealFileSystem(new File("d:")));  
  7. //3.通过虚拟目录获取文件资源  
  8. VirtualFile testFile = home.getChild("test.txt");  
  9. //4.通过一致的接口访问  
  10. Resource resource = new VfsResource(testFile);  
  11. if(resource.exists()) {  
  12.         dumpStream(resource);  
  13. }  
  14. System.out.println("path:" + resource.getFile().getAbsolutePath());  
  15. Assert.assertEquals(false, resource.isOpen());         
  16. }  
  17. @Test  
  18. public void testVfsResourceForJar() throws IOException {  
  19. //1.首先获取jar包路径  
  20.     File realFile = new File("lib/org.springframework.beans-3.0.5.RELEASE.jar");  
  21.     //2.创建一个虚拟的文件目录  
  22.     VirtualFile home = VFS.getChild("/home2");  
  23.     //3.将虚拟目录映射到物理的目录  
  24. VFS.mountZipExpanded(realFile, home,  
  25. TempFileProvider.create("tmp", Executors.newScheduledThreadPool(1)));  
  26. //4.通过虚拟目录获取文件资源  
  27.     VirtualFile testFile = home.getChild("META-INF/spring.handlers");  
  28.     Resource resource = new VfsResource(testFile);  
  29.     if(resource.exists()) {  
  30.             dumpStream(resource);  
  31.     }  
  32.     System.out.println("path:" + resource.getFile().getAbsolutePath());  
  33.     Assert.assertEquals(false, resource.isOpen());  
  34. }  
  35.    

 

       通过VFS,对于jar里的资源和物理文件系统访问都具有一致性,此处只是简单示例,如果需要请到Jboss官网深入学习。

 

 

 

4.3.1  ResourceLoader接口

 

       ResourceLoader接口用于返回Resource对象;其实现可以看作是一个生产Resource的工厂类。

 

java代码:
Java代码  收藏代码
  1. public interface ResourceLoader {  
  2.        Resource getResource(String location);  
  3.        ClassLoader getClassLoader();  
  4. }  

 

       getResource接口用于根据提供的location参数返回相应的Resource对象;而getClassLoader则返回加载这些Resource的ClassLoader。

 

       Spring提供了一个适用于所有环境的DefaultResourceLoader实现,可以返回ClassPathResource、UrlResource;还提供一个用于web环境的ServletContextResourceLoader,它继承了DefaultResourceLoader的所有功能,又额外提供了获取ServletContextResource的支持。

 

       ResourceLoader在进行加载资源时需要使用前缀来指定需要加载:“classpath:path”表示返回ClasspathResource,“http://path”和“file:path”表示返回UrlResource资源,如果不加前缀则需要根据当前上下文来决定,DefaultResourceLoader默认实现可以加载classpath资源,如代码所示(cn.javass.spring.chapter4.ResourceLoaderTest):

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void testResourceLoad() {  
  3.     ResourceLoader loader = new DefaultResourceLoader();  
  4.     Resource resource = loader.getResource("classpath:cn/javass/spring/chapter4/test1.txt");  
  5.     //验证返回的是ClassPathResource  
  6.     Assert.assertEquals(ClassPathResource.class, resource.getClass());  
  7.     Resource resource2 = loader.getResource("file:cn/javass/spring/chapter4/test1.txt");  
  8.     //验证返回的是ClassPathResource  
  9.     Assert.assertEquals(UrlResource.class, resource2.getClass());  
  10.     Resource resource3 = loader.getResource("cn/javass/spring/chapter4/test1.txt");  
  11.     //验证返默认可以加载ClasspathResource  
  12.     Assert.assertTrue(resource3 instanceof ClassPathResource);  
  13. }  

 

       对于目前所有ApplicationContext都实现了ResourceLoader,因此可以使用其来加载资源。

         ClassPathXmlApplicationContext:不指定前缀将返回默认的ClassPathResource资源,否则将根据前缀来加载资源;

         FileSystemXmlApplicationContext:不指定前缀将返回FileSystemResource,否则将根据前缀来加载资源;

         WebApplicationContext:不指定前缀将返回ServletContextResource,否则将根据前缀来加载资源;

         其他:不指定前缀根据当前上下文返回Resource实现,否则将根据前缀来加载资源。

 

4.3.2  ResourceLoaderAware接口

       ResourceLoaderAware是一个标记接口,用于通过ApplicationContext上下文注入ResourceLoader。

 

java代码:
Java代码  收藏代码
  1. public interface ResourceLoaderAware {  
  2.    void setResourceLoader(ResourceLoader resourceLoader);  
  3. }  

 

       让我们看下测试代码吧:

 

1)  首先准备测试Bean,我们的测试Bean还简单只需实现ResourceLoaderAware接口,然后通过回调将ResourceLoader保存下来就可以了:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter4.bean;  
  2. import org.springframework.context.ResourceLoaderAware;  
  3. import org.springframework.core.io.ResourceLoader;  
  4. public class ResourceBean implements ResourceLoaderAware {  
  5.     private ResourceLoader resourceLoader;  
  6.     @Override  
  7.     public void setResourceLoader(ResourceLoader resourceLoader) {  
  8.         this.resourceLoader = resourceLoader;  
  9.     }  
  10.     public ResourceLoader getResourceLoader() {  
  11.         return resourceLoader;  
  12.     }  
  13. }  
  14.    

 

2)  配置Bean定义(chapter4/resourceLoaderAware.xml):

 

java代码:
Java代码  收藏代码
  1.       
  2. <bean class="cn.javass.spring.chapter4.bean.ResourceBean"/>  

 

3)测试(cn.javass.spring.chapter4.ResoureLoaderAwareTest):

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void test() {  
  3.     ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceLoaderAware.xml");  
  4.     ResourceBean resourceBean = ctx.getBean(ResourceBean.class);  
  5.     ResourceLoader loader = resourceBean.getResourceLoader();  
  6.     Assert.assertTrue(loader instanceof ApplicationContext);  
  7. }  

 

       注意此处“loader instanceof ApplicationContext”,说明了ApplicationContext就是个ResoureLoader。

       由于上述实现回调接口注入ResourceLoader的方式属于侵入式,所以不推荐上述方法,可以采用更好的自动注入方式,如“byType”和“constructor”,此处就不演示了。   

 

4.3.3  注入Resource

       通过回调或注入方式注入“ResourceLoader”,然后再通过“ResourceLoader”再来加载需要的资源对于只需要加载某个固定的资源是不是很麻烦,有没有更好的方法类似于前边实例中注入“java.io.File”类似方式呢?

 

       Spring提供了一个PropertyEditor “ResourceEditor”用于在注入的字符串和Resource之间进行转换。因此可以使用注入方式注入Resource。

 

       ResourceEditor完全使用ApplicationContext根据注入的路径字符串获取相应的Resource,说白了还是自己做还是容器帮你做的问题。

 

接下让我们看下示例:

       1)准备Bean:

 

java代码:
Java代码  收藏代码
  1. package cn.javass.spring.chapter4.bean;  
  2. import org.springframework.core.io.Resource;  
  3. public class ResourceBean3 {  
  4.     private Resource resource;  
  5.     public Resource getResource() {  
  6.         return resource;  
  7.     }  
  8.     public void setResource(Resource resource) {  
  9.         this.resource = resource;  
  10.     }  
  11. }  

 

 

       2)准备配置文件(chapter4/ resourceInject.xml):

 

java代码:
Java代码  收藏代码
  1. <bean id="resourceBean1" class="cn.javass.spring.chapter4.bean.ResourceBean3">  
  2.    <property name="resource" value="cn/javass/spring/chapter4/test1.properties"/>  
  3. </bean>  
  4. <bean id="resourceBean2" class="cn.javass.spring.chapter4.bean.ResourceBean3">  
  5. <property name="resource"  
  6. value="classpath:cn/javass/spring/chapter4/test1.properties"/>  
  7. </bean>  

 

       注意此处“resourceBean1”注入的路径没有前缀表示根据使用的ApplicationContext实现进行选择Resource实现。

 

       3)让我们来看下测试代码(cn.javass.spring.chapter4.ResourceInjectTest)吧:

 

java代码:
Java代码  收藏代码
  1. @Test  
  2. public void test() {  
  3.     ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceInject.xml");  
  4.     ResourceBean3 resourceBean1 = ctx.getBean("resourceBean1", ResourceBean3.class);  
  5.     ResourceBean3 resourceBean2 = ctx.getBean("resourceBean2", ResourceBean3.class);  
  6.     Assert.assertTrue(resourceBean1.getResource() instanceof ClassPathResource);  
  7.     Assert.assertTrue(resourceBean2.getResource() instanceof ClassPathResource);  
  8. }  

 

 

       接下来一节让我们深入ApplicationContext对各种Resource的支持,及如何使用更便利的资源加载方式。

 

4.4.1  使用路径通配符加载Resource

       前面介绍的资源路径都是非常简单的一个路径匹配一个资源,Spring还提供了一种更强大的Ant模式通配符匹配,从能一个路径匹配一批资源。

 

       Ant路径通配符支持“?”、“*”、“**”,注意通配符匹配不包括目录分隔符“/”:

 

         “?”:匹配一个字符,如“config?.xml”将匹配“config1.xml”;

         “*”:匹配零个或多个字符串,如“cn/*/config.xml”将匹配“cn/javass/config.xml”,但不匹配匹配“cn/config.xml”;而“cn/config-*.xml”将匹配“cn/config-dao.xml”;

         “**”:匹配路径中的零个或多个目录,如“cn/**/config.xml”将匹配“cn /config.xml”,也匹配“cn/javass/spring/config.xml”;而“cn/javass/config-**.xml”将匹配“cn/javass/config-dao.xml”,即把“**”当做两个“*”处理。

 

Spring提供AntPathMatcher来进行Ant风格的路径匹配。具体测试请参考cn.javass.spring.chapter4. AntPathMatcherTest。

 

Spring在加载类路径资源时除了提供前缀“classpath:”的来支持加载一个Resource,还提供一个前缀“classpath*:”来支持加载所有匹配的类路径Resource。

 

Spring提供ResourcePatternResolver接口来加载多个Resource,该接口继承了ResourceLoader并添加了“Resource[] getResources(String locationPattern)”用来加载多个Resource:

 

java代码:
  1. public interface ResourcePatternResolver extends ResourceLoader {  
  2.        String CLASSPATH_ALL_URL_PREFIX = "classpath*:";  
  3.        Resource[] getResources(String locationPattern) throws IOException;  
  4. }  

 

Spring提供了一个ResourcePatternResolver实现PathMatchingResourcePatternResolver,它是基于模式匹配的,默认使用AntPathMatcher进行路径匹配,它除了支持ResourceLoader支持的前缀外,还额外支持“classpath*:”用于加载所有匹配的类路径Resource,ResourceLoader不支持前缀“classpath*:”:

 

首先做下准备工作,在项目的“resources”创建“META-INF”目录,然后在其下创建一个“INDEX.LIST”文件。同时在“org.springframework.beans-3.0.5.RELEASE.jar”和“org.springframework.context-3.0.5.RELEASE.jar”两个jar包里也存在相同目录和文件。然后创建一个“LICENSE”文件,该文件存在于“com.springsource.cn.sf.cglib-2.2.0.jar”里。

 

 

一、“classpath”: 用于加载类路径(包括jar包)中的一个且仅一个资源;对于多个匹配的也只返回一个,所以如果需要多个匹配的请考虑“classpath*:”前缀;

 

java代码:
  1. @Test  
  2. public void testClasspathPrefix() throws IOException {  
  3.     ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();  
  4.     //只加载一个绝对匹配Resource,且通过ResourceLoader.getResource进行加载  
  5.     Resource[] resources=resolver.getResources("classpath:META-INF/INDEX.LIST");  
  6.     Assert.assertEquals(1, resources.length);  
  7.     //只加载一个匹配的Resource,且通过ResourceLoader.getResource进行加载  
  8.     resources = resolver.getResources("classpath:META-INF/*.LIST");  
  9.     Assert.assertTrue(resources.length == 1);             
  10. }  

 

二、“classpath*”: 用于加载类路径(包括jar包)中的所有匹配的资源。带通配符的classpath使用“ClassLoader”的“Enumeration<URLgetResources(String name)”方法来查找通配符之前的资源,然后通过模式匹配来获取匹配的资源。如“classpath:META-INF/*.LIST”将首先加载通配符之前的目录“META-INF”,然后再遍历路径进行子路径匹配从而获取匹配的资源。

 

java代码:
  1. @Test  
  2. public void testClasspathAsteriskPrefix () throws IOException {  
  3.      ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        
  4.      //将加载多个绝对匹配的所有Resource  
  5.     //将首先通过ClassLoader.getResources("META-INF")加载非模式路径部分  
  6.     //然后进行遍历模式匹配  
  7.     Resource[] resources=resolver.getResources("classpath*:META-INF/INDEX.LIST");  
  8.     Assert.assertTrue(resources.length > 1);      
  9.     //将加载多个模式匹配的Resource  
  10.     resources = resolver.getResources("classpath*:META-INF/*.LIST");  
  11.     Assert.assertTrue(resources.length > 1);    
  12. }  

 

注意“resources.length >1”说明返回多个Resource。不管模式匹配还是非模式匹配只要匹配的都将返回。

 

       在“com.springsource.cn.sf.cglib-2.2.0.jar”里包含“asm-license.txt”文件,对于使用“classpath*: asm-*.txt”进行通配符方式加载资源将什么也加载不了“asm-license.txt”文件,注意一定是模式路径匹配才会遇到这种问题。这是由于“ClassLoader”的“getResources(String name)”方法的限制,对于name为“”的情况将只返回文件系统的类路径,不会包换jar包根路径。

 

 

java代码:
  1. @Test  
  2. public void testClasspathAsteriskPrefixLimit() throws IOException {  
  3.     ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();      //将首先通过ClassLoader.getResources("")加载目录,  
  4.     //将只返回文件系统的类路径不返回jar的跟路径  
  5.     //然后进行遍历模式匹配  
  6.     Resource[] resources = resolver.getResources("classpath*:asm-*.txt");  
  7.     Assert.assertTrue(resources.length == 0);  
  8.     //将通过ClassLoader.getResources("asm-license.txt")加载  
  9.     //asm-license.txt存在于com.springsource.net.sf.cglib-2.2.0.jar  
  10.     resources = resolver.getResources("classpath*:asm-license.txt");  
  11.     Assert.assertTrue(resources.length > 0);       
  12.     //将只加载文件系统类路径匹配的Resource  
  13.     resources = resolver.getResources("classpath*:LICENS*");  
  14.     Assert.assertTrue(resources.length == 1);  
  15. }  

 

对于“resolver.getResources("classpath*:asm-*.txt");”,由于在项目“resources”目录下没有所以应该返回0个资源;“resolver.getResources("classpath*:asm-license.txt");”将返回jar包里的Resource;“resolver.getResources("classpath*:LICENS*");”,因为将只返回文件系统类路径资源,所以返回1个资源。

 

因此加载通配符路径时(即路径中包含通配符),必须包含一个根目录才能保证加载的资源是所有的,而不是部分。

 

 

三、“file”:加载一个或多个文件系统中的Resource。如“file:D:/*.txt”将返回D盘下的所有txt文件;      

 

四、无前缀:通过ResourceLoader实现加载一个资源。

 

AppliacationContext提供的getResources方法将获取资源委托给ResourcePatternResolver实现,默认使用PathMatchingResourcePatternResolver。所有在此就无需介绍其使用方法了。

 

4.4.2  注入Resource数组

       Spring还支持注入Resource数组,直接看配置如下:

 

java代码:
  1. <bean id="resourceBean1" class="cn.javass.spring.chapter4.bean.ResourceBean4">  
  2. <property name="resources">  
  3.         <array>  
  4.             <value>cn/javass/spring/chapter4/test1.properties</value>  
  5.             <value>log4j.xml</value>  
  6.         </array>  
  7.     </property>  
  8. </bean>  
  9. <bean id="resourceBean2" class="cn.javass.spring.chapter4.bean.ResourceBean4">  
  10. <property name="resources" value="classpath*:META-INF/INDEX.LIST"/>  
  11. </bean>  
  12. <bean id="resourceBean3" class="cn.javass.spring.chapter4.bean.ResourceBean4">  
  13. <property name="resources">  
  14.         <array>  
  15.             <value>cn/javass/spring/chapter4/test1.properties</value>  
  16.             <value>classpath*:META-INF/INDEX.LIST</value>  
  17.         </array>  
  18.     </property>  
  19. </bean>  

 

       “resourceBean1”就不用多介绍了,传统实现方式;对于“resourceBean2”则使用前缀“classpath*”,看到这大家应该懂的,加载匹配多个资源;“resourceBean3”是混合使用的;测试代码在“cn.javass.spring.chapter4.ResourceInjectTest.testResourceArrayInject”。

       Spring通过ResourceArrayPropertyEditor来进行类型转换的,而它又默认使用“PathMatchingResourcePatternResolver”来进行把路径解析为Resource对象。所有大家只要会使用“PathMatchingResourcePatternResolver”,其它一些实现都是委托给它的,比如AppliacationContext的“getResources”方法等。

 

4.4.3  AppliacationContext实现对各种Resource的支持

       一、ClassPathXmlApplicationContext:默认将通过classpath进行加载返回ClassPathResource,提供两类构造器方法:

 

java代码:
  1. public class ClassPathXmlApplicationContext {  
  2.     //1)通过ResourcePatternResolver实现根据configLocation获取资源  
  3.        public ClassPathXmlApplicationContext(String configLocation);  
  4.        public ClassPathXmlApplicationContext(String... configLocations);  
  5.        public ClassPathXmlApplicationContext(String[] configLocations, ……);  
  6.         
  7.     //2)通过直接根据path直接返回ClasspathResource  
  8.        public ClassPathXmlApplicationContext(String path, Class clazz);  
  9.        public ClassPathXmlApplicationContext(String[] paths, Class clazz);  
  10.        public ClassPathXmlApplicationContext(String[] paths, Class clazz, ……);  
  11. }  

 

       第一类构造器是根据提供的配置文件路径使用“ResourcePatternResolver ”的“getResources()”接口通过匹配获取资源;即如“classpath:config.xml”

       第二类构造器则是根据提供的路径和clazz来构造ClassResource资源。即采用“public ClassPathResource(String path, Class<?> clazz)”构造器获取资源。

 

 

       二、FileSystemXmlApplicationContext:将加载相对于当前工作目录的“configLocation”位置的资源,注意在Linux系统上不管“configLocation”是否带“/”,都作为相对路径;而在window系统上如“D:/resourceInject.xml”是绝对路径。因此在除非很必要的情况下,不建议使用该ApplicationContext。

 

java代码:
  1. public class FileSystemXmlApplicationContext{  
  2.        public FileSystemXmlApplicationContext(String configLocation);  
  3.        public FileSystemXmlApplicationContext(String... configLocations,……);  
  4. }  

 

 

 

java代码:
  1. //linux系统,以下全是相对于当前vm路径进行加载  
  2. new FileSystemXmlApplicationContext("chapter4/config.xml");  
  3. new FileSystemXmlApplicationContext("/chapter4/confg.xml");  
  1. //windows系统,第一个将相对于当前vm路径进行加载;  
  2. //第二个则是绝对路径方式加载  
  3. new FileSystemXmlApplicationContext("chapter4/config.xml");  
  4. new FileSystemXmlApplicationContext("d:/chapter4/confg.xml");  

 

 

       此处还需要注意:在linux系统上,构造器使用的是相对路径,而ctx.getResource()方法如果以“/”开头则表示获取绝对路径资源,而不带前导“/”将返回相对路径资源。如下:

 

java代码:
  1. //linux系统,第一个将相对于当前vm路径进行加载;  
  2. //第二个则是绝对路径方式加载  
  3. ctx.getResource ("chapter4/config.xml");  
  4. ctx.getResource ("/root/confg.xml");  
  5. //windows系统,第一个将相对于当前vm路径进行加载;  
  6. //第二个则是绝对路径方式加载  
  7. ctx.getResource ("chapter4/config.xml");  
  8. ctx.getResource ("d:/chapter4/confg.xml");  

 

       因此如果需要加载绝对路径资源最好选择前缀“file”方式,将全部根据绝对路径加载。如在linux系统“ctx.getResource ("file:/root/confg.xml");”    

 

分享到:
评论

相关推荐

    Spring实战之ResourceLoader接口资源加载用法示例

    主要介绍了Spring实战之ResourceLoader接口资源加载用法,结合实例形式分析了Spring使用ResourceLoader接口加载资源的相关配置与使用技巧,需要的朋友可以参考下

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    Spring 2.0 开发参考手册

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    Spring API

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    spring chm文档

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    spring源代码

    //只加载一个绝对匹配Resource,且通过ResourceLoader.getResource进行加载 Resource[] resources = resolver.getResources("classpath:META-INF/INDEX.LIST"); Assert.assertEquals(1, resources.length); //...

    update-resourceloader-config-plugin:使用webpack入口点的前端模块更新mediawiki的resourceloader配置的extension.json

    npm install --save-dev update-resourceloader-config-plugin 然后在您的webpack.config.js添加到插件中: var conf = {entry : {'resources/ext.gather.special.collection/init' : './resources/ext.gather....

    Springboot项目打war包docker包找不到resource下静态资源的解决方案

    今天小编就为大家分享一篇关于Springboot项目打war包docker包找不到resource下静态资源的解决方案,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

    resource-loader:从JAR中获取文件或加载共享库很困难。 我们变得很容易

    资源加载器即使是从JAR内部或JAR外部进行加载,Resource Loader也为您提供了加载resource文件的功能。 资源加载器还支持通过SharedLibraryLoader加载共享库。安装这是在基于Gradle的项目中安装库的方法。 // Top-...

    SPRING API 2.0.CHM

    ResourceLoader ResourceLoaderAware ResourceMapFactoryBean ResourceOverridingShadowingClassLoader ResourcePatternResolver ResourcePatternUtils ResourceScriptSource ResourceServlet ResourceUtils...

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    Spring Boot读取resources目录文件方法详解

    主要介绍了Spring Boot读取resources目录文件方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    webx3框架指南PDF教程附学习Demo

    • 扩展性 —— Webx 3.0对Spring做了扩展,使Spring Bean不再是“bean”,而是升级成“组件”。一个组件可以扩展另一个组件,也可以被其它组件扩展。这种机制造就了Webx的非常好的扩展性,且比未经扩展的Spring更易...

    spring 2.5 注解

    @BackgroundTaskpublic class FilePoller {@PostConstructpublic void startPolling() {...}@... @Autowiredprivate ResourceLoader resourceLoader; @Autowiredprivate ApplicationContext applicationContext;

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    offline-http-sync

    如果资源不可用,它将从缓存中加载文件。 这用于资源的可用性比其一致性更重要的情况。 用法 通过http加载一些资源,保存到缓存,如果http资源不可用,则仅从缓存加载。 var sl = require('offline-http-sync'); ...

    blink.java9.resource:从Java9模块加载资源

    $ javac -d target/classes src/main/java/it/nipe/java9/ResourceLoader.java 建造 ~/Spikespaces/java9/resource $ jar --create --file target/javanove.jar --main-class it.nipe.java9.ResourceLoader -C ...

    velocity-engine-core-2.0.jar

    // 按文件加载 // ve.init(); // Template t = ve.getTemplate("src/velocity/hellovelocity.vm"); ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); // 在类路径下加载 ve.setProperty(...

    java7源码-coder-tools:开发者的工具箱,包含各种第三方类库,示例

    java7 源码 coder-tools是程序员对一些新类库、新技术、新特性研究的笔记,为了让您更快更容易对这些新东西...loadJarFile:ResourceLoader DataBinder Manual-Aop Guava Retry和Spring retry cache-library redis-je

Global site tag (gtag.js) - Google Analytics