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

时间:2021-10-27 08:09:17
基于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. 访问,浏览器显示webapp/resources/test.png

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

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

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

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

  现在servlet-context.xml为:

  < xml version="1.0" encoding="UTF-8" >
<beans:beans xmlns=""
xmlns:xsi=""
xmlns:beans=""
xmlns:context=""
xmlns:mvc=""
xsi:schemaLocation="
rg/schema/beans
pringframework.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>

  访问,浏览器上显示的正是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这个URL是等效的。

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

  INFO :
org.springframeworthod.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.springframeworthod.annotation.RequestMappingHandlerMapping - Looking
up handler method for path /simple
TRACE: org.springframeworthod.annotation.RequestMappingHandlerMapping -
Found 1 matching mapping(s) for [/simple] :
[{[/simple],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}]

  访问

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

  访问(杜撰的后缀名)

  DEBUG:
org.springframeworthod.annotation.RequestMappingHandlerMapping - Looking
up handler method for path /simple.foo
TRACE: org.springframeworthod.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>

条回答

基于Spring MVC的Web应用开发(三) - Resources的更多相关文章

  1. 基于Spring框架的Web应用开发笔记 - Outline

    Motivation 最近的工作涉及Web框架搭建,在了解公司原有采用框架基础上对Web开发技术栈做了一次升级,在次做记录. Audience J2EE Web Application Develop ...

  2. Spring boot 基于Spring MVC的Web应用和REST服务开发

    Spring Boot利用JavaConfig配置模式以及"约定优于配置"理念,极大简化了基于Spring MVC的Web应用和REST服务开发. Servlet: package ...

  3. Java高级架构师(一)第13节:Spring MVC实现Web层开发

    package com.sishuok.architecture1.customermgr.web; import org.springframework.beans.factory.annotati ...

  4. 基于Spring MVC的web应用随应用启动而加载

    写个类实现org.springframework.context.ApplicationContextAware接口即可. 但是如下的程序会在启动时加载两次: @Controller public c ...

  5. 基于Vue&plus;Spring MVC&plus;MyBatis&plus;Shiro&plus;Dubbo开发的分布式后台管理系统

    本文项目代码: 服务端:https://github.com/lining90567/dubbo-demo-server 前端:https://github.com/lining90567/dubbo ...

  6. Spring MVC第一课:用IDEA构建一个基于Spring MVC&comma; Hibernate&comma; My SQL的Maven项目

    作为一个Spring MVC新手最基本的功夫就是学会如何使用开发工具创建一个完整的Spring MVC项目,本文站在一个新手的角度讲述如何一步一步创建一个基于Spring MVC, Hibernate ...

  7. 构建一个基于 Spring 的 RESTful Web Service

    本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...

  8. 基于Spring MVC 实现拦截器

    Spring MVC 拦截器 一,具体内容: 在所有的开发之中拦截器属于一个重要的组件,可以说几乎所有的项目都会提供的概念应用,不管是Spring MVC,还是Struts 2.x都是提供有拦截器的, ...

  9. spring mvc构建WEB应用程序入门例子

    在使用spring mvc 构建web应用程序之前,需要了解spring mvc 的请求过程是怎样的,然后记录下如何搭建一个超简单的spring mvc例子. 1) spring mvc的请求经历 请 ...

随机推荐

  1. k8s入门系列之guestbook快速部署

    k8s集群以及一些扩展插件已经安装完毕,本篇文章介绍一下如何在k8s集群上快速部署guestbook应用. •实验环境为集群:master(1)+node(4),详细内容参考<k8s入门系列之集 ...

  2. linux-13基础命令之-touch&comma;mkdir

    1.  touch 命令 用于创建空白文件与修改文件时间,格式:touch[选项][文件]: linux 下文件时间有三种 @1.更改时间(mtime):内容修改时间: @2.更改权限(ctime): ...

  3. mssql server提示无权限

    mssqlserver在查询系统视图时(如:select * from sys.syscacheobjects),有时会报出如下提示: 消息 300,级别 14,状态 1,第 1 行VIEW SERV ...

  4. redis系列之redis是什么

    一.简介 REmote DIctionary Server(Redis),redis是一个基于内存的单机key/value系统,类似memcached,但支持value为多种形式,包括:字符串(str ...

  5. Ubuntu中安装DiscuzX2

    http://blog.csdn.net/kevin_ysu/article/details/7452938 一.Apache的安装 Apache作为一个功能强大的Web程序,自然是架建Web服务器的 ...

  6. APP纯黑盒测试---某些可以试试的操作

    一.多次快速点击一处功能入口: 该测试方法可以在某些应用中打开俩次目标界面,举一些具体一点的例子: 1.比如现在很多APP需要登陆,如果打开了俩次登录页面,就容易造成登录成功后应用跳转界面又是登录界面 ...

  7. Java基础总结--异常处理机制

    ----异常的概述-----1.异常,就是不正常的现象,可能发生在编译期间也可能发生在运行期间2.可能会出现不同的异常,进而在Java中对其描述封装为类--在这些异常类中抽取其共性的东西(异常发生的位 ...

  8. 简单docker镜像修改方式

    • 创建Dockerfile,文件内容如下: FROM nps:v1.0.1 ENTRYPOINT ["/usr/bin/init.sh"] • 启动基础镜像:docker run ...

  9. 6、Android Content Provider测试

    如果你的应用中使用了Content Provider来与其他应用进行数据交互,你需要对Content Provider进行测试来确保正常工作. 创建Content Provider整合测试 在Andr ...

  10. 最简单的操作 jetty IDEA 【debug】热加载

    [博客园cnblogs笔者m-yb原创,转载请加本文博客链接,笔者github: https://github.com/mayangbo666,公众号aandb7,QQ群927113708] http ...