springMVC对静态资源访问的处理


  1. <servlet>  

  2.   <servlet-name>mvc</servlet-name>  

  3.   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  

  4.   <init-param>  

  5.     <param-name>contextConfigLocation</param-name>  

  6.     <param-value>/WEB-INF/classes/conf/spring/mvc-*.xml</param-value>  

  7.   </init-param>  

  8.   <load-on-startup>1</load-on-startup>  

  9. </servlet>  

  10.   

  11. <servlet-mapping>  

  12.   <servlet-name>mvc</servlet-name>  

  13.   <url-pattern>/</url-pattern>  

  14. </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 先拦截。 

但是这样还会有个问题,就是无法访问到classpath下的资源文件,看了tomcat的DefaultServlet的配置项,似乎也没有可以指定目录的地方。
    ◇ 方案二:Spring 3.0.4 以后版本提供了 <mvc:resources /> 

    

  1. <!-- 处理静态资源 -->  

  2.     <!-- 上传的图片缓存1个月,其他js,css,img资源缓存一年 -->  

  3.     <mvc:resources mapping="/res/**" location="/res/" cache-period="2592000"/>   

  4.     <mvc:resources mapping="/resources/**" location="/resources/" cache-period="31536000"/>   

  5.     <mvc:resources mapping="/css/**" location="/css/" cache-period="31536000"/>  

  6.     <mvc:resources mapping="/js/**" location="/js/" cache-period="31536000"/>  

  7.     <mvc:resources mapping="/img/**" location="/img/" cache-period="31536000"/>  

  8.     <mvc:resources mapping="/images/**" location="/images/" cache-period="31536000"/>  

       mapping 映射到 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。

另外,性能最好的应该是直接利用容器的DefaultServlet,让它最先拦截静态资源请求,这样就避免了后续的转发等操作,提高了性能,但是无法访问classpath下的资源文件。而通过mvc:resources标签可以简单配置匹配规则和资源文件路径,应该说是最简单快捷的一种方式,当然这大概也是mvc命名空间设计的初衷。

另外,若想结合两者的话,自己倒是可以尝试写一个Servlet来处理,不过估计有难度且麻烦。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。