• spring mvc处理静态资源


    servlet的url映射定义为'/'表示映射全部路径

    struts的过滤器是*.action,在spring mvc中设置成*.action或者*.do......也是可以的,但是spring mvc就是霸气,它就是要处理一切路径.

    对于静态资源,在tomcat服务器上,必须通过servlet才能访问,除此之外别无他法.

    即便是你自己不写servlet来访问静态资源,tomcat无论如何也要找到一个servlet来处理静态资源,所以别觉得用servlet处理会慢,无论如何都要用servlet来处理.

    一言以蔽之,在tomcat容器中,处理资源的基本单位是servlet,进行数据操作计算的基本单位也是servlet.

    前人之述备矣,天下文章一大抄.

       Spring MVC 中的核心 servlet - DispatcherServlet,我们在 web.xml 文件中通常这样定义:

    <servlet>  
      <servlet-name>mvc</servlet-name>  
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
      <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>/WEB-INF/classes/conf/spring/mvc-*.xml</param-value>  
      </init-param>  
      <load-on-startup>1</load-on-startup>  
    </servlet>  
      
    <servlet-mapping>  
      <servlet-name>mvc</servlet-name>  
      <url-pattern>/</url-pattern>  
    </servlet-mapping>  

            很明显,该 servlet 对应的 url-pattern 定义成 /,因此该 servlet 会匹配上诸如 /images/a.jpg, /css/hello.css 等这些静态资源,甚至包括 /jsp/stock/index.jsp 这些 jsp 也会匹配。但是并没有定义相应的 Controller 来处理这些资源,因此这些请求通常是无法完成的。
            很麻烦?没有 Struts 方便?此言差矣,因此你在 Struts 定义其核心 filter 的 url-pattern 是 *.action,当然不会对处理 jsp 和静态资源操作影响了。Spring MVC 若也定义类似的 url-pattern,同样不存在问题。
            说到这里,我们应该想一个问题。Tomcat 中,只有 servlet 能够处理请求,即使是 jsp,也会被编译成 servlet。我们即便使用 Struts,定义 *.action 的url-pattern,那 .css, *.gfi 等这些静态资源到底是谁来处理了???你可不要想当然的认为我不是输入了图片的路径了吗?如,/images/a/b/c.gif。请注意,servlet 容器中,只有 servlet 采用处理资源!
            由 servlet 处理这些资源那是一定了。不过,不同的 servlet 容器/应用服务器,处理这些静态资源的 servlet 的名字不大一样:

    • Tomcat, Jetty, JBoss, and GlassFish:默认 Servlet 名字为 "default"
    • Google App Engine:默认 Servlet 名字为 "_ah_default"
    • Resin:默认 Servlet 名字为 "resin-file"
    • WebLogic:默认 Servlet 名字为 "FileServlet"
    • WebSphere:默认 Servlet 名字为 "SimpleFileServlet"

        ◇ 方案一:激活 Tomcat 的 defaultServlet 来处理静态资源

    <servlet-mapping>  
      <servlet-name>default</servlet-name>  
      <url-pattern>*.jpg</url-pattern>  
    </servlet-mapping>  
    <servlet-mapping>  
      <servlet-name>default</servlet-name>  
      <url-pattern>*.js</url-pattern>  
    </servlet-mapping>  
    <servlet-mapping>  
      <servlet-name>default</servlet-name>  
      <url-pattern>*.css</url-pattern>  
    </servlet-mapping>  

            每种类型的静态资源需要分别配置一个 servlet-mapping,同时,要写在 DispatcherServlet 的前面, 让 defaultServlet 先拦截。
        ◇ 方案二:Spring 3.0.4 以后版本提供了 <mvc:resources />

     <mvc:resources location="/resources/" mapping="/resources/**"/>   

            /resources/** 映射到 ResourceHttpRequestHandler 进行处理,location 指定静态资源的位置,可以是 web application 根目录下、jar 包里面,这样可以把静态资源压缩到 jar 包中。cache-period 可以使得静态资源进行 web cache。
            使用 <mvc:resources /> 元素,会把 mapping 的 URI 注册到 SimpleUrlHandlerMapping 的 urlMap 中,key 为 mapping 的 URI pattern 值,而 value 为 ResourceHttpRequestHandler,这样就巧妙的把对静态资源的访问由 HandlerMapping 转到 ResourceHttpRequestHandler 处理并返回,所以就支持 classpath 目录, jar 包内静态资源的访问。
        ◇ 方案三:使用 <mvc:default-servlet-handler />
            <mvc:default-servlet-handler /> 会把 "/**" url 注册到 SimpleUrlHandlerMapping 的 urlMap 中,把对静态资源的访问由 HandlerMapping 转到 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler 处理并返回。DefaultServletHttpRequestHandler 使用就是各个 Servlet 容器自己的默认 Servlet。
            补充说明下以上提到的 HandlerMapping 的 order 的默认值:

    • DefaultAnnotationHandlerMapping:0
    • <mvc:resources /> 自动注册的 SimpleUrlHandlerMapping:2147483646
    • <mvc:default-servlet-handler/> 自动注册的 SimpleUrlHandlerMapping:2147483647

            Spring 会先执行 order 值比较小的。当访问一个 a.jpg 图片文件时,先通过 DefaultAnnotationHandlerMapping 来找处理器,一定是找不到的,我们没有叫 a.jpg 的 Controller。再按 order 值升序找,由于最后一个 SimpleUrlHandlerMapping 是匹配 "/**" 的,所以一定会匹配上,再响应图片。

            Spring MVC 中,访问一个图片,还要走层层匹配。性能肯定好不到哪里去。不仅仅是 Spring MVC,即便 Struts,它们毕竟存活于 servlet 容器,只要由 servlet 容器处理这些静态资源,必然要将这些资源读入 JVM 的内存区中。所以,处理静态资源,我们通常会在前端加 apache 或 nginx。

    其中处理静态资源的类是org.springframework.web.servlet.resource.ResourceHttpRequestHandler,而且在location的描述中说明Each location must point to a valid directory. 即每个location都必须指向一个有效的目录。

    下面看一下org.springframework.web.servlet.resource.ResourceHttpRequestHandler中是如何处理静态资源请求的: 

    首先它实现了org.springframework.web.HttpRequestHandler这个接口的handleRequest方法:

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
            throws ServletException, IOException {  
        // 这里根据配置来设置缓存的header  
        checkAndPrepare(request, response, true);  
      
        // 获取要获取的资源,如果不存在,直接返回404错误  
        Resource resource = getResource(request);  
        if (resource == null) {  
            logger.debug("No matching resource found - returning 404");  
            response.sendError(HttpServletResponse.SC_NOT_FOUND);  
            return;  
        }  
      
        // 省略部分代码……  
        // 返回相应的资源,即把资源文件写到响应中  
        writeContent(response, resource);  
    }  

    而getResource方法实现如下:

    protected Resource getResource(HttpServletRequest request) {  
        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);  
        // 省略部分处理和检验路径的代码……  
      
        // 这里循环从配置的location下查找请求的静态资源  
        for (Resource location : this.locations) {  
            try {  
                if (logger.isDebugEnabled()) {  
                    logger.debug("Trying relative path [" + path + "] against base location: " + location);  
                }  
                Resource resource = location.createRelative(path);  
                // 判断资源存在而且能够读取  
                if (resource.exists() && resource.isReadable()) {  
                    // 这里就是3.2.12及以后处理上的差别,在3.2.12前,不会判断该资源是否在指定的路径下,直接就返回了resource,而3.2.12及以后做了如下判断  
                    if (isResourceUnderLocation(resource, location)) {  
                        if (logger.isDebugEnabled()) {  
                            logger.debug("Found matching resource: " + resource);  
                        }  
                        return resource;  
                    }  
                    else {  
                        if (logger.isTraceEnabled()) {  
                            logger.trace("resource="" + resource + "" was successfully resolved " +  
                                    "but is not under the location="" + location);  
                        }  
                        return null;  
                    }  
                }  
                else if (logger.isTraceEnabled()) {  
                    logger.trace("Relative resource doesn't exist or isn't readable: " + resource);  
                }  
            }  
            catch (IOException ex) {  
                logger.debug("Failed to create relative resource - trying next resource location", ex);  
            }  
        }  
        return null;  
    }  

    下面看看isResourceUnderLocation的实现:

    private boolean isResourceUnderLocation(Resource resource, Resource location) throws IOException {  
        if (!resource.getClass().equals(location.getClass())) {  
            return false;  
        }  
        String resourcePath;  
        String locationPath;  
        if (resource instanceof UrlResource) {  
            resourcePath = resource.getURL().toExternalForm();  
            locationPath = location.getURL().toExternalForm();  
        }  
        else if (resource instanceof ClassPathResource) {  
            resourcePath = ((ClassPathResource) resource).getPath();  
            locationPath = ((ClassPathResource) location).getPath();  
        }  
        else if (resource instanceof ServletContextResource) {  
            resourcePath = ((ServletContextResource) resource).getPath();  
            locationPath = ((ServletContextResource) location).getPath();  
        }  
        else {  
            resourcePath = resource.getURL().getPath();  
            locationPath = location.getURL().getPath();  
        }  
        // 这里是对路径的处理,如果我们配置的是/res/**,那么直接拼接成了/res/**/,如果请求资源为/res/jquery.js,
    //那么会判断res/jquery.js是否以/res/**/开头,如果不是,则返回该location下没有该资源,导致不能404错误
    locationPath = (locationPath.endsWith("/") || !StringUtils.hasLength(locationPath) ? locationPath : locationPath + "/"); if (!resourcePath.startsWith(locationPath)) { return false; } if (resourcePath.contains("%")) { // Use URLDecoder (vs UriUtils) to preserve potentially decoded UTF-8 chars... if (URLDecoder.decode(resourcePath, "UTF-8").contains("../")) { if (logger.isTraceEnabled()) { logger.trace("Resolved resource path contains "../" after decoding: " + resourcePath); } return false; } } return true; }

    '/'表示根目录,以'/'结尾的路径表示文件夹,location为'/'表示该路径下的全部文件相当于'/**','/*'表示单级目录,所以'/'=='/**'>'/*'

    mvc:resource可以设置多个属性

    在@RequestMapping注解中,如果没有参数,则表示这个servlet是默认的servlet,会处理一切资源.

       <!-- 对静态资源文件的访问  方案一  -->  
        <mvc:default-servlet-handler/>  
          
        <!-- 对静态资源文件的访问  方案二 -->  
    <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>  
    <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>  
    <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/> 
    <!-- 对静态资源文件的访问  方案二的简便写法--> 
     <mvc:resources location="/" mapping="/" /> 
  • 相关阅读:
    Socket开发框架之消息的回调处理
    Socket开发框架之数据加密及完整性检查
    Socket开发框架之数据传输协议
    Socket开发框架之框架设计及分析
    C#进行Visio二次开发之文件导出及另存Web页面
    Winform混合式开发框架的特点总结
    代码生成工具Database2Sharp中增加视图的代码生成以及主从表界面生成功能
    基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/5517645.html
Copyright © 2020-2023  润新知