`

()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");”    

 

分享到:
评论

相关推荐

    U盘量产工具SM3280&3281&3282-AvidiaV0209整合版

    U盘量产工具FLASH量产工具SM3280&3281&3282-AvidiaV0209整合版

    java课程期末考试.zip

    java课程期末考试

    分布式消息中间件,参考kafka,未完成.zip

    分布式消息中间件,参考kafka,未完成

    修木工施工规范及流程.docx

    修木工施工规范及流程.docx

    汽车电子中MICROSAR OBD协议栈解决方案及其应用

    内容概要:本文详细介绍了VECTOR提供的MICROSAR OBD协议栈解决方案,涵盖了OBD模块、ECU支持、监控功能和服务请求等方面的内容。此外,还讨论了OBD在不同国家和地区的技术标准与法规要求,以及MICROSAR OBD解决方案的优势,如适应不同项目的需求和高度集成于AUTOSAR 4平台。 适合人群:汽车电子工程师、软件开发者、汽车制造商及相关行业从业人员。 使用场景及目标:① 适用于车辆诊断系统的开发和维护;②帮助工程师理解和掌握OBD协议的具体实施方法和应用场景;③ 提供了一个成熟、可扩展的解决方案,用于满足OBD相关标准和法规的要求。 其他说明:本文不仅提供了技术层面的详细解析,还探讨了实际操作过程中可能遇到的问题和解决方案。同时强调了屏蔽信息过载的重要性,提醒工程师保持内心平静,专注做好本职工作。

    适用于 Python 的 LINE 消息 API SDK.zip

    适用于 Python 的 LINE 消息 API SDK适用于 Python 的 LINE Messaging API 的 SDK。介绍适用于 Python 的 LINE Messaging API SDK 可以轻松使用 LINE Messaging API 开发机器人,您可以在几分钟内创建一个示例机器人。文档请参阅官方 API 文档了解更多信息英语https //developers.line.biz/en/docs/messaging-api/overview/日语https://developers.line.biz/ja/docs/messaging-api/overview/要求Python >= 3.9安装$ pip 安装 line-bot-sdk概要用法from flask import Flask, request, abortfrom linebot.v3 import ( WebhookHandler)from linebot.v3.exceptions import ( InvalidSig

    Java字节码工程工具包.zip

    Java字节码工程工具包Javassist 版本 3版权所有 (C) 1999-2023 Shigeru Chiba,保留所有权利。Javassist(JAVA 编程助手)使 Java 字节码操作变得简单。它是一个用于编辑 Java 字节码的类库它使 Java 程序能够在运行时定义新类并在 JVM 加载类文件时对其进行修改。与其他类似的字节码编辑器不同,Javassist 提供两个级别的 API源代码级别和字节码级别。如果用户使用源代码级别 API,他们可以编辑类文件而无需了解 Java 字节码的规范。整个 API 仅使用 Java 语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码Javassist 会即时编译它。另一方面,字节码级别 API 允许用户像其他编辑器一样直接编辑类文件。该软件根据 Mozilla 公共许可证版本 1.1、GNU 宽通用公共许可证版本 2.1 或更高版本或 Apache 许可证版本 2.0 分发。文件README.md 此自述文件。Changes.md 发行说明。License.html 许可证文件。tuto

    毕设源码-基于python的西西家居全屋定制系统的设计与实现_ijsj--论文-期末大作业+说明文档.rar

    本项目是基于Python语言开发的西西家居全屋定制系统,旨在为家居行业提供一个高效、智能的定制解决方案。项目涵盖了从客户需求分析、设计方案生成、材料选购到最终订单生成的全过程,力求实现家居定制的数字化和智能化。 在主要功能方面,系统具备强大的客户管理模块,能够详细记录和分析客户的定制需求。设计模块则采用先进的三维建模技术,为客户提供直观、真实的家居设计方案预览。此外,系统还整合了丰富的材料数据库,方便客户根据自身喜好和预算进行材料选择。 框架方面,项目采用了B/S架构,确保了系统的稳定性和可扩展性。后端使用Python的Django框架,前端则结合了HTML、CSS和JavaScript等技术,实现了用户界面的友好和响应速度。 开发此项目的目的,不仅是为了满足家居行业对个性化定制的需求,也为计算机相关专业的学生提供了一个实践和学习的平台,有助于提升他们的实际开发能力。

    Javascript 是数字化创新的起点,是语言的基础,也是基本概念 .zip

    Javascript 是数字化创新的起点,是语言的基础,也是基本概念。Basecamp JavascriptJavascript 是数字化创新的起点,是语言的基础,也是基本概念。嵌套存储库,可作为启动项下待办事项的实践活动。

    已弃用 - Coinbase Python API.zip

    已弃用 — Coinbase Python APICoinbase Coinbase API V2的官方 Python 库。重要提示此库当前针对的是 API V2,而 OAuth 客户端需要 V2 权限(即wallet:accounts:read)。如果您仍在使用 API V1,请使用此库的旧版本。特征接近 100% 的测试覆盖率。支持API Key + Secret和OAuth 2身份验证。调用 API 的便捷方法 - 为您打包 JSON!自动将 API 响应解析为相关的 Python 对象。使用IPython时,所有对象都具有可制表完成的方法和属性。安装coinbase可以在PYPI上使用。使用以下命令安装pippip install coinbase或者easy_installeasy_install coinbase该库目前针对 Python 版本 2.7 和 3.4+ 进行了测试。注意此软件包名称过去是指George Sibble维护的非官方 coinbase_python 库。George 慷慨地允许我们使用此软件包

    基于RBAC权限控制的基础后台.zip

    基于RBAC权限控制的基础后台

    毕设源码-python-基于Python爬虫的网络小说数据分析系统的设计与实现-期末大作业+说明文档.rar

    本项目是基于Python爬虫的网络小说数据分析系统的设计与实现,旨在为计算机相关专业的大学生提供一个实践平台,特别是在毕业设计和项目实战练习方面。项目通过Python强大的网络爬虫技术,从流行的网络小说网站自动抓取数据,包括书籍信息、章节内容、用户评论等。 主要功能涵盖数据采集、数据清洗、数据存储和数据分析。数据采集模块利用Scrapy等爬虫框架高效抓取网页内容;数据清洗模块确保数据的准确性和一致性;数据存储则采用MySQL等数据库系统,便于数据管理和查询;数据分析模块通过Pandas、NumPy等工具进行数据处理和分析,生成多维度的统计报告和可视化图表。 此项目不仅帮助学生掌握Python编程和网络爬虫技术,还能让他们深入了解数据分析的全过程,提升解决实际问题的能力。同时,系统的实现和应用也反映了现代信息技术在文学创作和消费领域的应用价值和潜力。

    ssm框架Java项目源码-基于Java的在线日语培训平台的设计与实现+jsp毕设-大作业.zip

    本项目是一个基于Java的在线日语培训平台的设计与实现,采用SSM框架(Spring+SpringMVC+MyBatis)进行开发,旨在为计算机相关专业的学生提供一个实践和学习的平台,同时也为日语学习者提供一个在线学习的空间。项目中主要功能涵盖了用户管理、课程管理、学习资源上传下载、在线测试与反馈等多个方面。通过该平台,教师能够轻松管理课程内容和学生信息,学生则可以随时随地访问学习资源,参与在线课程和测试,从而提高学习效率和兴趣。 在开发此项目的过程中,我们重点关注了系统的可维护性和可扩展性,确保代码结构清晰,便于后续的功能迭代和优化。此外,通过使用SSM框架,实现了前后端的分离,提高了开发效率和系统的响应速度。该项目不仅能够满足毕设的需求,还能作为Java学习者提升编程能力和实践经验的实用工具。

    基于java的机票管理系统设计与实现.docx

    基于java的机票管理系统设计与实现.docx

    基于Java实现的数据结构设计源码学习指南

    该项目为《基于Java实现的数据结构设计源码》,共包含51个文件,主要由46个Java源文件构成,辅以2个文本文件、1个Git忽略文件、1个许可证文件以及1个XML文件,全面涵盖了数据结构设计的核心内容。

    绿色食品 水稻生产操作规程.docx

    绿色食品 水稻生产操作规程.docx

    这款出色的应用程序可以纠正您之前的控制台命令 .zip

    他妈的 Fuck是一款出色的应用程序,其灵感来自@liamosaur 的 推文,它可以纠正以前控制台命令中的错误。The Fuck太慢了吗?试试实验性的即时模式!更多示例➜ apt-get install vimE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?➜ fucksudo apt-get install vim [enter/↑/↓/ctrl+c][sudo] password for nvbn:Reading package lists... Done...➜ git pushfatal: The current branch master has no upstream branch.To push the current branch and set the remote

    全国大学生FPGA创新设计竞赛作品 泡罩包装药品质量在线检测平台.zip

    全国大学生FPGA创新设计竞赛作品 “泡罩包装药品质量在线检测平台“.zip

    桃苗木质量基本要求表.docx

    桃苗木质量基本要求表.docx

    使用 Python 漂亮地打印表格数据,这是一个库和一个命令行实用程序 存储库从 bitbucket.org,astanin,python-tabulate 迁移而来 .zip

    使用 Python 漂亮地打印表格数据,这是一个库和一个命令行实用程序。存储库从 bitbucket.org/astanin/python-tabulate 迁移而来。python-tabulate使用 Python、库和命令行实用程序漂亮地打印表格数据。该库的主要用例是轻松打印小表格只需一个函数调用,格式由数据本身引导为轻量级纯文本标记创作表格数据多种输出格式适合进一步编辑或转换混合文本和数字数据的可读表示智能列对齐、可配置数字格式、小数点对齐安装要安装 Python 库和命令行实用程序,请运行pip install tabulate命令行实用程序将在 Linux 上安装为(例如tabulate)或者在 Windows 上的 Python 安装中安装为(例如)。bin/usr/bintabulate.exeScriptsC:\Python39\Scripts\tabulate.exe您可以考虑仅为当前用户安装该库pip install tabulate --user在这种情况下,命令行实用程序将安装到 ~/.local/bin/tabula

Global site tag (gtag.js) - Google Analytics