`

基于Spring MVC的Web应用开发(3) - Resources

 
阅读更多

上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件。

注意到本项目的web.xml配置的DispatcherServlet对应的url-pattern为"/",即所有的URL请求都会经过Spring MVC的处理。实际的Web项目有大量的资源文件,如javascript文件,css文件,png,jpg等图片文件,甚至是Flash等等,我们没有必要对这些静态文件的访问都设置对应的URL,那样会造成大量重复性的劳动,以及维护上的复杂性。Spring MVC提供了一种机制,可以映射一种URL和一个location,此URL后面接的静态文件,对应着location目录下对应的静态文件。此配置为:

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

说明一下,

1. 访问http://localhost:8080/resources/test.png,浏览器显示webapp/resources/test.png

2. 访问http://localhost:8080/resources/scripts/test.js,浏览器显示webapp/resources/scripts/test.js

3. 访问http://localhost:8080/resources/css/2012/test.css,浏览器显示webapp/resources/css/2012/test.css

注意到mapping的值"/resources/**"有两个*,它表示映射resources/下所有的URL,包括子路径(即接多个/),如上面的1、2、3,如果只有一个*,将只能映射1级路径,即只能访问1,访问2、3将会报错。

很遗憾,如果只加这一行,带有@Controller类里面的@RequestMapping映射都不会生效。我是搜索到stackoverflow上的这个帖子联想到解决方案的,后来在stackoverflow搜到另外一个帖子有个还算比较详细的解释,帖子上说使用<mvc:resources/>时必须添加<mvc:annotation-driven/>,然后带有@Controller注解类的@RequestMapping映射信息才能被读取到。

现在servlet-context.xml为:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
	<resources mapping="/resources/**" location="/resources/" />
	
	<!-- Imports user-defined @Controller beans that process client requests -->
	<beans:import resource="controllers.xml" />
	
	<!-- You have to add this because you had a <resources/> declare -->
	<mvc:annotation-driven/>
	
</beans:beans>

访问http://localhost:8080/web/resources/test.js,浏览器上显示的正是test.js的内容。

 

下面解决HelloWorld那篇文章中遗留的一个问题,在那一篇文章中,@RequestMapping只有一个"/simple",但从日志中发现,有三种URL"/simple","/simple.*","/simple/"映射

INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController'
INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController'
INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'

看看DefaultAnnotationHandlerMapping,这个类有个useDefaultSuffixPattern成员变量,默认为true,即默认识别一个URL的3种URL变种,在addUrlsForPath方法中如果if条件成立(useDefaultSuffixPattern=true),就加入了另外两种映射("/simple.*","/simple/"),

因此访问http://localhost:8080/web/simple/http://localhost:8080/web/simple.html,甚至http://localhost:8080/web/simple.foo(杜撰的后缀名)时,实际上和http://localhost:8080/web/simple这个URL是等效的。

 

处理完HelloWorld的遗留问题,很自然地,看看在增加了<mvc:resources/>后,启动日志中是如何映射的:

INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/simple],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple()

不再是DefaultAnnotationHandlerMapping,而换成了RequestMappingHandlerMapping。

我们改变一下思路,从请求端看看SpringMVC如何处理上面说的三种URL。

访问http://localhost:8080/web/simple

DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /simple
TRACE: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Found 1 matching mapping(s) for [/simple] : [{[/simple],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}]

 访问http://lcoalhost:8080/web/simple.html

DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /simple.html
TRACE: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Found 1 matching mapping(s) for [/simple.html] : [{[/simple.*],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}]

 访问http://lcoalhost:8080/web/simple.foo(杜撰的后缀名

DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /simple.foo
TRACE: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Found 1 matching mapping(s) for [/simple.foo] : [{[/simple.*],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}]

  很奇妙,SpringMVC自动的识别了三种URL,这一切是怎么做到的呢?启动日志明明只Mapped了一个URL嘛。

还是要看看RequestMappingHandlerMapping的源代码,有两个属性useSuffixPatternMatch和useTrailingSlashMatch,

useSuffixPatternMatch(使用后缀模式匹配)默认为true,即可以识别"/simple.*",

useTrailingSlashMatch(末尾斜线匹配)默认为true,即可以识别"/simple/"。

最后注意到两次启动日志打印的类并不相同,

前面一个为DefaultAnnotationHandlerMapping,后面一个为RequestMappingHandlerMapping,这又是为什么呢?

原因就在加入了<mvc:resources/>后,Spring工厂对Bean的选型改变了,

没加<mvc:resouces/>前,SpringMVC使用默认策略,所以DefaultAnnotationHandlerMapping被使用了,

加了<mvc:resources/>后,SpringMVC不再使用默认策略,而是使用了RequestMappingHandlerMapping这个类,这应该是在源代码中写死的。

 

====================================================================

 

[补充说明,可不看,翻译的也很渣]Spring Reference Document(Spring官方手册)的16.14.5 Configuraing Serving of Resources这一节中对于Resources的介绍:

 

这个配置(<mvc:resources/>)给ResourceHttpRequestHandler配置了Resource位置,这样handler就可以处理一个特殊的URL模式对应的静态资源请求。它提供了一个很方便的方法直接从物理路径访问到静态资源,包括web应用的root路径和classpath上的路径。cache-period属性可以被用来设置未来可能会用上的实验性的header(可能一年以后吧,这要看诸如Page Speed和YSlow这样的优化工具的给力程度了),这样可以让它被客户端更有效的使用。这个handler也恰当地评估了Last-Modified这个header(如果有的话),因此304状态码(HTTP状态码,如我们常知道浏览器上返回的404错误 译者)会恰当的返回,避免已经被客户端缓存的资源再次访问服务端,造成负载。比如,要使用/resources/**这样的URL模式请求访问一个web应用的root内部public-resources目录下的服务端资源,你可以使用:

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
      
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
  }

}

XML中的相同配置在:

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

下面对处理资源的配置可以满足刚才说的一年后的实验性特性,确保了浏览器缓存的最大化使用率和减少浏览器发起的HTTP请求:

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
      
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926);
  }

}

 同样在XML中:

<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/>

 mapping这个属性必须是一个Ant模式(不太清楚什么是Ant模式 译者),该Ant模式可以被SimpleHandlerMapping使用,location属性必须指定一个或者多个有效的资源目录位置。多个资源位置可以通过使用逗号分隔符的列表值指定。指定的位置会根据任何给定的请求的资源的表现,按照一个特定的顺序被检查一遍。比如为了使我们即能访问web应用root路径又能访问classpath路径下任何一个jar包里一个已知的/META-INF/public-web-resources/路径,我们这样写:

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
      
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**")
      .addResourceLocations("/", "classpath:/META-INF/public-web-resources/");
  }

}

 在XML中:

<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/>

当资源有可能随着新版本的应用的发布而改变时,推荐你将一个版本字符串放进请求资源时使用的映射模式中,因此你可以强制客户端请求应用资源中部署的新版本。这样一个版本字符串使用SpEL配置,因此当部署新版本时,它可以很容易的在单个地方管理起来。

举个例子,我们考虑一个应用,它在生产环境使用一个性能优化过的定制的Dojo JavaScript库的构件,这个构件通常在一个web应用中的/public-resources/dojo/dojo.js路径下部署。因为对于应用的每个新版本,Dojo的不同部分可能会合并成一个定制构件,客户端浏览器需要强制性的重新下载定制构件dojo.js资源,只要一个新版本的应用被部署了。一个简单的打包方式就是在一个.properties配置文件中管理应用的版本,比如:

application.version=1.0.0

 然后在一个bean中使用<util:properties/>标签来让properties文件中的值可以被SpEL访问到:

<util:properties id="applicationProps" location="/WEB-INF/spring/application.properties"/>

现在通过SpEL,application.version可以访问了,我们可以将它合并到<resource/>标签里。

<mvc:resources mapping="/resources-#{applicationProps['application.version']}/**" location="/public-resources/"/>

 在Java类中,你可以使用@PropertiesSource注解,然后注入Environment抽象类,来访问到所有预定义的属性值:

@EnableWebMvc
@Configuration
@PropertySource("/WEB-INF/spring/application.properties")
public class WebConfig extends WebMvcConfigurerAdapter {

  @Inject Environment env;

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources-" + env.getProperty("application.version") + "/**")
      .addResourceLocations("/public-resources/");
  }

}

最后,为了使用合适的URL请求资源,我们可以利用Spring JSP标签:(在JSP页面中使用的 译者)

<spring:eval expression="@applicationProps['application.version']" var="applicationVersion"/>

<spring:url value="/resources-{applicationVersion}" var="resourceUrl">
    <spring:param name="applicationVersion" value="${applicationVersion}"/>
</spring:url>

<script src="${resourceUrl}/dojo/dojo.js" type="text/javascript"> </script>
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论
2 楼 baso4233 2012-10-30  
  拜读
1 楼 karvenlin 2012-07-13  
如果把静态资源放入到WEB-INF中怎么进行配置,在页面中如何来引用?
[img][/img]

相关推荐

    springboot学习思维笔记.xmind

    Spring-Webmvc-Portlet 数据访问/集成(DataAccess/Intefration) Spring-JDBC Spring-TX Spring-ORM Spring-OXM Spring-JMS Spring的生态 Spring Boot ...

    spring mvc后台管理系统

    简单后台管理系统 基于maven管理,整合最新spring mvc 4.3.3.RELEASE版整合了mybatis 3.4.1,涵盖了目前互联网web系统最流行的组件,log4j2日志、freemarker模板、protostuff序列化、fastjson、redis、spring-data-...

    activiti 基础 web项目 spring mvc

    4.程序中使用的是spring mvc注解的方式,可扩展的地方也很多,我是从网上下的maven项目,然后自己改成web项目,除了整个架子外,其他的都是我自己的东西 5.目前能实现的功能就是走一个流程,适合初学者研究 我深知...

    Spring.MVC.Cookbook.1784396419

    Over 40 recipes for creating cloud-ready Java web applications with Spring MVC About This Book Configure Spring MVC to build logic-less controllers that transparently support the most advanced web ...

    基于框架的Web开发-第一个springMVC的例子.doc

    和mvc同一层次的resources文件夹,里面放系统配置文件applicationContext.xml顶层包mvc,下面分三个子包domain,service和web,mvc.web包里放控制器类WEB-INF文件夹下放置web.xml文件和mySpring-servlet.xml文件Web...

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

    13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 13.4. ...

    开发用jar包合集

    spring-webmvc-5.0.6.RELEASE-javadoc.jar spring-webmvc-5.0.6.RELEASE-sources.jar spring-webmvc-5.0.6.RELEASE.jar spring-websocket-5.0.6.RELEASE-javadoc.jar spring-websocket-5.0.6.RELEASE-sources....

    spring-boot-reference.pdf

    27.1. The “Spring Web MVC Framework” 27.1.1. Spring MVC Auto-configuration 27.1.2. HttpMessageConverters 27.1.3. Custom JSON Serializers and Deserializers 27.1.4. MessageCodesResolver 27.1.5. Static...

    spring-boot-webmvc-jpa

    Spring Boot示例 Hibernate模式和数据 实体的ddl模式application setting : spring.jpa.hibernate.ddl-auto: create-drop 通过import.sql插入数据path= src/main/resources/import.sql 跑步 mvn spring-boot:run

    spring framework4

    Spring MVC and Spring WebFlux web frameworks Integration: remoting, JMS, JCA, JMX, email, tasks, scheduling, cache. Languages: Kotlin, Groovy, dynamic languages. Minimum requirements JDK 8+ for Spring...

    Spring 2.0 开发参考手册

    13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 ...

    IDEA下通过maven整合spring、spring mvc、mybatis三大框架

    在IDEA环境下使用maven创建web项目,并整合spring、spring mvc、mybatis三个框架。实现前端点击超链接,访问数据库功能。注释比较全面。 拿到项目之后,找到resources文件夹下的account.sql执行一下,创建数据库表。...

    springmvc+hibernate+spring+easyui开发bsalse进销存后台管理系统源代码下载

    项目运行地址:http://localhost:8080/(无需加... 数据库初始化:数据库文件位置resources/sql/mysql/*.sql 另:项目所用jar都在项目的lib文件下,导入到myeclipse下,直接运行发布项目即可(用户名、密码均为admin)

    spring-boot-shopping-cart:使用Spring Boot + Thymeleaf制作的简单购物车Web应用程序

    Spring Boot购物车Web应用程序关于这是一个练习Spring + Thymeleaf的演示项目。 这个想法是建立一些基本的购物车网络应用程序。 它是使用Spring Boot , Spring Security , Thymeleaf , Spring Data JPA , Spring...

    Spring 5 Recipes, 4th Edition

    Finally, Spring web recipes cover Spring MVC, other dynamic scripting, integration with the popular Grails Framework (and Groovy), REST/web services, and more. You’ll also see recipes on new topics...

    Spring.REST.1484208242

    You will learn about several Spring projects such as Spring Boot, Spring MVC, Spring Data JPA, and Spring Security and the role they play in simplifying REST application development. You will learn ...

    spring chm文档

    13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 ...

    Spring3中配置DBCP,C3P0,Proxool,Bonecp数据源

    在Spring3中配置数据源,包括DBCP,C3P0,Proxool,Bonecp主要的数据源,里面包含这些数据源的jar文件和依赖文件及配置文件。。 如Bonecp目前听说是最快的数据源,速度是传统的c3p0的25倍, bonecp.properties文件: ...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    SSH 为 Struts+Spring+Hibernate的一个集成框架,是目前较流行的一种Web应用程序开源框架。其中使用Struts作为系统的整体基础架构,负责MVC的分离,在Struts框架的模型部分,控制业务跳转,利用Hibernate框架对持久...

    Spring API

    13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 ...

Global site tag (gtag.js) - Google Analytics