zoukankan      html  css  js  c++  java
  • Spring如何处理资源以及如何在Spring中使用资源

    Java标准的java.net.URL主要用于各种URL前缀的类和标准处理程序,但是不足以满足对低级资源的所有访问。例如,没有标准化的URL实现可以用来访问类路径或相对于ServletContext的资源。虽然可以为专门的URL前缀注册新的处理程序(类似于http:)等前缀的现有处理程序,但这通常相当复杂,并且URL接口仍然缺少一些需要的功能,例如检查所指向的资源是否存在的方法。

    Resource接口

    Spring的Resource接口是一个更强大的接口,用于抽象对低级资源的访问。下表显示了资源接口定义:

    public interface Resource extends InputStreamSource {
    
        boolean exists();
    
        boolean isOpen();
    
        URL getURL() throws IOException;
    
        File getFile() throws IOException;
    
        Resource createRelative(String relativePath) throws IOException;
    
        String getFilename();
    
        String getDescription();
    
    }

    Resource接口扩展了InputStreamSource接口

    public interface InputStreamSource {
    
        InputStream getInputStream() throws IOException;
    
    }

    Resource接口中一些最重要的方法是:

    • getInputStream():定位并打开资源,从资源中读取,返回InputStream。预期每次调用都会返回一个新的InputStream。调用者有责任关闭流。
    • exists():返回一个布尔值,标识此资源是否实际物理存在。
    • isOpen():返回一个布尔值,表示此资源是否表示具有开放流的句柄。如果为true,则无法多次读取InputStream,必须只读取一次,然后关闭以避免资源泄漏。对于所有常见的资源实现返回false,InputStreamResource除外。
    • getDescription():返回此资源的说明,在使用该资源时用于错误输出。这通常是完全限定的文件名或资源的实际URL。

    其他方法允许你获得表示资源的实际URL或File对象(如果底层实现兼容并支持该功能)。

    Spring本身广泛地使用Resource抽象,它是许多方法签名中的参数。一些spring api中的其他方法(例如各种ApplicationContext实现的构造函数)采用一个字符串,该字符串以简单的形式用于创建与该上下文实现相适应的资源,或者通过字符串路径上的特殊前缀,让调用者指定必须创建和使用特定的Resource实现。

    内置Resource实现

    Spring包含以下这些实现:

    • UrlResource
    • ClassPathResource
    • FileSystemResource
    • ServletContextResource
    • InputStreamResource
    • ByteArrayResource
    UrlResource

    UrlResource包装了java.net.URL,通过URL访问任何对象,例如文件、HTTP目标、FTP目标等。所有的URL都有一个标准化的字符串表示,因此使用适当的标准化前缀来表示一个URL类型和另一个URL类型。这包括file:用于访问文件系统路径,http:用于通过http协议访问资源,ftp:用于通过ftp访问资源,等等。

    UrlResource是由Java代码通过显式使用UrlResource构造函数创建的,但通常是在调用一个API方法时隐式创建的,该方法采用字符串参数来表示路径。对于后一种情况,JavaBeans属性编辑器最终决定要创建哪种类型的资源。如果路径字符串包含已知的前缀(例如,classpath:),它将为该前缀创建适当的专用资源。但是,如果它不识别前缀,它将假定该字符串是标准的URL字符串并创建一个UrlResource。

    ClassPathResource

    此类表示从类路径获取的资源。它使用线程上下文类加载器、给定类加载器或给定类来加载资源。

    此Resource实现支持解析在类路径下的java.io.File文件,而不是驻留在jar中且尚未扩展到文件系统(通过servlet引擎或其他环境)的类路径资源。为了解决这个问题,各种Resource实现总是支持解析作为java.net.URL.

    ClassPathResource是由Java代码通过显式使用ClassPathResource构造函数创建的,但通常是在调用一个API方法时隐式创建的,该方法采用字符串参数表示路径。对于后一种情况,JavaBeans属性编辑器识别字符串路径上的特殊前缀classpath:,并在这种情况下创建一个ClassPathResource。

    FileSystemResource

    支持java.io.File 和java.nio.file.Path的一个Resource实现。

    ServletContextResource

    ServletContext资源的Resource实现,它解析相关web应用程序根目录中的相对路径。

    它始终支持流访问和URL访问,但允许java.io.File文件仅当web应用程序存档扩展并且资源实际位于文件系统上时才进行访问。不管在文件系统上还是直接从JAR或其他地方(比如数据库)访问,实际上取决于Servlet容器。

    InputStreamResource

    InputStreamResource是给定InputStream的Resource实现。只有在没有具体的资源实现适用的情况下才应该使用它。在可能的情况下,首选ByteArrayResource或任何基于文件的资源实现。

    与其他资源实现不同,这是一个已打开资源的描述符。因此,它从isOpen()返回true。如果需要将资源描述符保存在某个地方,或者需要多次读取一个流,请不要使用它。

    ByteArrayResource

    字节数组的Resource实现。它为给定的字节数组创建一个ByteArrayInputStream。

    它对于从任何给定的字节数组加载内容非常有用,而不必求助于一次使用的InputStreamResource。

    以下是几个Resource实现的实例代码:

    @RunWith(SpringRunner.class)
    @WebAppConfiguration
    @ContextHierarchy({
            @ContextConfiguration(classes = SpringConfig.class),
            @ContextConfiguration(classes = SpringMVCConfig.class)
    })
    public class ResourceTest {
    
        @Autowired
        private WebApplicationContext webApplicationContext;
    
        @Test
        public void testUrlResource() throws IOException {
            UrlResource resource = new UrlResource("http://www.baidu.com");
            InputStream inputStream = resource.getInputStream();
            System.out.println(getStringFromInputStream(inputStream));
        }
    
        @Test
        public void testClassPathResource() throws IOException {
            ClassPathResource resource = new ClassPathResource("dmd.properties");
            InputStream inputStream = resource.getInputStream();
            System.out.println("文件描述:" + resource.getDescription());
            System.out.println(getStringFromInputStream(inputStream));
        }
    
        @Test
        public void testFileSystemResource() throws IOException {
            FileSystemResource resource =  new FileSystemResource("C:\large.txt");
            InputStream inputStream = resource.getInputStream();
            System.out.println(getStringFromInputStream(inputStream));
        }
    
        @Test
        public void testServletContextResource() throws IOException {
            // 读取webapp下的文件
            ServletContextResource resource =  new ServletContextResource(webApplicationContext.getServletContext(), "WEB-INF/views/index.jsp");
            InputStream inputStream = resource.getInputStream();
            System.out.println(getStringFromInputStream(inputStream));
        }
    
        public static String getStringFromInputStream(InputStream inputStream) {
            String resultData = null;      //需要返回的结果
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] data = new byte[1024];
            int len = 0;
            try {
                while ((len = inputStream.read(data)) != -1) {
                    byteArrayOutputStream.write(data, 0, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            resultData = new String(byteArrayOutputStream.toByteArray());
            return resultData;
        }
    }
    ResourceLoader

    ResourceLoader接口可以返回Resource实例

    public interface ResourceLoader {
    
        Resource getResource(String location);
    
    }

    所有应用程序上下文都实现ResourceLoader接口。因此,可以使用所有应用程序上下文来获取资源实例。

    当你对特定的应用程序上下文调用getResource(),而指定的位置路径没有特定的前缀时,你将获得一个适合该特定应用程序上下文的资源类型。例如,假设对ClassPathXmlApplicationContext实例执行以下代码片段:

    Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

    对于ClassPathXmlApplicationContext,该代码返回ClassPathResource。如果对FileSystemXmlApplicationContext实例执行相同的方法,它将返回一个FileSystemResource。对于WebApplicationContext,它将返回一个ServletContextResource。类似地,它将为每个上下文返回适当的对象。

    因此,您可以以适合特定应用程序上下文的方式加载资源。

    另一方面,您也可以通过指定特殊的classpath:prefix来强制使用ClassPathResource,而不考虑应用程序上下文类型:

    Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

    类似地,你可以通过指定任何标准来强制使用UrlResourcejava.net.URL前缀。以下两个示例使用文件前缀和http前缀:

    Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
    
    Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");

    下表总结了将字符串对象转换为资源对象的策略:

    PrefixExampleExplanation

    classpath:

    classpath:com/myapp/config.xml

    从类路径加载。

    file:

    file:///data/config.xml

    从文件系统查找。

    http:

    https://myserver/logo.png

    从网络URL查找

    (none)

    /data/config.xml

    取决于基础应用程序上下文。

    ResourceLoaderAware 接口

    ResourceLoaderAware是一个特殊的接口(感知接口),获取资源加载器,可以获得外部资源。

    public interface ResourceLoaderAware {
    
        void setResourceLoader(ResourceLoader resourceLoader);
    }

    当一个类实现ResourceLoaderware并部署到应用程序上下文(作为Spring托管bean)中时,应用程序上下文将其识别为ResourceLoaderware。然后应用程序上下文调用setResourceLoader(ResourceLoader),将自身作为参数提供(记住,Spring中的所有应用程序上下文都实现ResourceLoader接口)。

    由于ApplicationContext是一个资源加载器,bean还可以实现ApplicationContextAware接口,并直接使用提供的应用程序上下文来加载资源。但是,一般来说,如果你只需要专用的ResourceLoader接口,那么最好使用它。代码将只耦合到资源加载接口(可以认为是实用程序接口),而不是整个Spring ApplicationContext接口。

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

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

    构造应用程序上下文
    ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
    
    ApplicationContext ctx =
        new FileSystemXmlApplicationContext("conf/appContext.xml");
    
    ApplicationContext ctx =
        new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
    应用程序上下文构造函数资源路径中的通配符

    应用程序上下文构造函数值中的资源路径可以是简单路径,每个路径都有到目标资源的一对一映射,或者可以包含特殊的“classpath*:”前缀或内部Ant样式的正则表达式(通过使用Spring的PathMatcher实用程序进行匹配)。后者都是有效的通配符。

    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文件的内容来解析通配符。

    对可移植性的影响

    如果指定的路径已经是一个文件URL(隐式地因为基ResourceLoader是文件系统URL,也可能是显式的),那么通配符保证以完全可移植的方式工作。

    classpath*:前缀

    在构造基于XML的应用程序上下文时,位置字符串可以使用特殊的classpath*:前缀,如下例所示:

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

    这种特殊前缀指定必须获得与给定名称匹配的所有类路径资源(在内部,这实际上是通过调用类加载器.getResources(…)然后合并以形成最终的应用程序上下文定义。

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

  • 相关阅读:
    GridView“GridView1”激发了未处理的事件“RowDeleting”
    遮罩层提示框,可拖动标题栏(兼容FF)
    JS判断IE,FF等浏览器类型
    DataGrid GridView 隔行换色 鼠标经过改变背景色
    常用js函数Common.js
    GridView和DataFormatString 日期格式 精确小数点后位数
    常用js函数CheckData.js
    图像处理技术
    VS Code中编写html(5) 标签的布局设置
    产品经理应该具备的技能(1)
  • 原文地址:https://www.cnblogs.com/myitnews/p/13301959.html
Copyright © 2011-2022 走看看