紧接着上文,我们来学习mvc标签。
Spring版本:4.3.14。
6.mvc 命名空间
mvc命名空间内的标签是用于Spring MVC的配置,一般情况下Spring的配置文件和Spring-MVC的配置文件都是分开配置的,Spring MVC的配置文件默认情况下是 dispatcher-servlet.xml,所以mvc的配置通常都是在该配置文件中。下面我们来学习下每个标签的用处。
首先,记得先引入命名空间,并且先搭建好Spring MVC环境,这些都很简单,这里就不多说了。
6.1 <mvc:annotation-driven>
标签
mvc:annotation-driven
标签是一种简写的形式,用于为Spring MVC分发请求到Controller上,不过该标签支持的功能很多,比如@InitBinder数据绑定,@NumberFormatannotation数据格式化,@DateTimeFormat日期格式化,数据校验,@RequestBody @ResponseBody的json支持等;下面来看一下该标签中各个属性的配置。
6.1.1 ignore-default-model-on-redirect
元素
顾名思义,用于请求重定向时是否忽略model的参数,布尔值,true和false,默认是false,但文档中建议应该设置为true:
The default setting is false but new applications should consider setting it to true.
有的时候,我们重定向的时候需要传参数,我们通过Model
添加参数,通过ModelAttribute
来获取URL的参数,但有些时候在重定向的时候,我们不想把参数带过去,比如说我们重定向的地址是:http://localhost:8080/hello/test.html?users=user1&users=user2&users=user3&users=user4...
,这时候我们可以通过配置该属性为true,来忽略URL后面这一堆参数。
具体使用可参考:https://stackoverflow.com/questions/13247239/spring-mvc-controller-redirect-without-parameters-being-added-to-my-url%3C/b%3E
6.1.2 enable-matrix-variables
元素
一般情况下,我们URL中很少会出现name-value这种类似于键值对的形式,而Spring MVC却提供了对这种形式的支持,在Spring MVC中,它们被称为matrix-variables。而如果要在Spring中使用该功能,需要先配置enable-matrix-variables
属性,该属性是布尔类型,有true和false,默认情况下是false,也就是不开启的状态。
matrix variables的形式如:
/cars;color=red,green;year=2012
,其中各个变量之间使用分号分隔,每个变量如果多个值,使用逗号分割;简单看下官网的几个例子:
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
这里简单说下,GetMapping是Spring4.3之后的一个组合注解。我们在URL中配置相应的name-value组合之后,可以通过注解@MatrixVariable来获取该组合的值。如果要查看更多有关matrix-variables相关,可参考官网文件:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-matrix-variables
6.1.3 conversion-service
元素
在我们通过浏览器向我们的服务器提交数据的时候,也就是在字段绑定期间,用于类型转换的ConversionService的bean的名称,默认情况下的实现是FormattingConversionService
,如果我们需要自定义转换类型的时候可以通过配置该bean指向我们自定义的转换器来实现。
自定义转换器:
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String dateString) {
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
引入我们的转换器,当然我们开可以配置多个转换器:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="expend.DateConverter"/>
</set>
</property>
</bean>
其实Spring已经给我们提供了许多种类型转换器,位于org.springframework.core.convert.support
目录下,在以上转换器都不满足的情况下,我们可以自定义我们的类型转换器;并且使用的时候可以结合@InitBinder注解来一并学习使用。
6.1.4 message-codes-resolver
元素
一般情况下,在浏览器传递参数到服务器的时候,我们都需要进行数据绑定,并且进行参数验证,如果绑定失败或者验证失败,对应的错误码会保存到Spring的错误对象中,而我们可以通过MessageCodesResolver来对错误code构建相应的message信息。默认情况下,Spring将使用DefaultMessageCodesResolver
对象进行解析,当然如果有需要,我们也可以自定义我们的解析器。
6.1.5 validator
元素
上面我们也已经说过,对于从浏览器端传递过来的参数,前端的JS可以涵盖大部分的校验职责,但为了避免用户绕过浏览器,使用HTTP工具向后台直接发送一些非正常的数据,我们后台的校验也是必须的。
而在这一点上,Java中的JSR303/JSR-349定义了相应的规范,而Bean Validation则是该规范的具体实现。Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator:
- Bean Validation API是Java定义的一个验证参数的规范。
- Hibernate Validator是Bean Validation API的一个实现。
而我们如果在Spring中使用该框架的话,只需要简单配置即可:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 如果不加默认,则到使用classpath下的 ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<!-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找 -->
<value>classpath:messages</value>
<value>classpath:org/hibernate/validator/ValidationMessages</value>
</list>
</property>
<property name="useCodeAsDefaultMessage" value="false"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
其中消息文件 ValidationMessages.properties 定义了我们配置的一些信息:
user.id.null=用户编号不能为空
user.name.null=用户名不能为空
user.name.length.illegal=用户名长度必须在5到20之间
user.name.illegal=用户名必须是字母
user.password.null=密码不能为空
以上参考自:http://jinnianshilongnian.iteye.com/blog/1990081
6.1.6 content-negotiation-manager
元素
该元素中文一般翻译为内容协商,意思是同一资源可以多种展示方式,比如说当输入后缀是html的时候,返回html页面,当输入json后缀的时候,返回j'son格式的数据,当然是在同一个方法里,其实就是根据请求规则决定返回什么样的内容类型。
比如说类似于这种:@RequestMapping(value={"/user/{id}","/user/{id}.json"})
,也就是说当输入user/1的时候返回对应的试图,当输入user/1.json的时候,返回json格式的数据,这种就是所谓的内容协商了,而Spring中content-negotiation-manager
该元素就是用于实现这个功能的,当然不单单是json,还有其他方式,比如XML等等。
在RESTful该特性的实现方式大概有以下三种:
- 1.使用http request header: Accept
GET /user/123 HTTP/1.1
Accept: application/xml //将返回xml格式数据
GET /user/123 HTTP/1.1
Accept: application/json //将返回json格式数据
- 使用扩展名
/user/123.xml 将返回xml格式数据
/user/123.json 将返回json格式数据
/user/123.html 将返回html格式数据
- 使用参数
/user/123?format=xml //将返回xml数据
/user/123?format=json //将返回json数据
而Spring则是支持以上三种方式的,ContentNegotiatingViewResolver是根据客户提交的MimeType(如 text/html,application/xml)来跟服务端的一组viewResover的MimeType相比较,如果符合,即返回viewResover的数据。
而 /user/123.xml, ContentNegotiatingViewResolver会首先将 .xml 根据mediaTypes属性将其转换成 application/xml,然后完成前面所说的比较。
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<!-- 设置为true以忽略对Accept Header的支持-->
<property name="ignoreAcceptHeader" value="true"/>
<!-- 在没有扩展名时即: "/user/1" 时的默认展现形式 -->
<property name="defaultContentType" value="text/html"/>
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
<!-- 配置ContentNegotiatingViewResolver -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
</bean>
如果想更多的了解内容协商相关的问题,可参考:
Spring MVC Content Negotiation
https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc/
官网文档-mvc-config-content-negotiation
6.1.7 mvc:argument-resolvers
子标签
该标签用于自定义参数解析器,比如说我们通过@RequestBody来解析json格式的参数,通过@RequestParam来解析表单相关的参数,如果Spring自带的参数解析器满足不了我们的需要,我们可以自定义解析器,实现HandlerMethodArgumentResolver
接口,然后配合注解来完成自定义解析参数的功能。
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="..."/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
6.1.8 mvc:return-value-handlers
子标签
该标签表示允许我们对特定的返回类型做处理,看一个例子就明白了:、
@RequestMapping("/testReturnHandlers")
public User testHandlerReturnMethod(){
User u = new User();
u.setUserName("test");
return u;
}
由于上面testReturnHandlers接口返回的是一个pojo类型,正常情况下Spring MVC无法解析,最后会由DefaultRequestToViewNameTranslator
解析处一个缺省的view name,转到 testReturnHandlers.jsp,如果没有的话就会报错。这时候我们就可以通过该标签引入我们自定义的 HandlerMethodReturnValueHandler的实现来完成数据的处理。
这时候我们先自定义我们的返回Handler处理器:
public class UserHandlers implements HandlerMethodReturnValueHandler {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> type = returnType.getParameterType();
if(User.class.equals(type))
{
return true;
}
return false;
}
@Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
logger.info("handler for return type users ");
mavContainer.setViewName("helloworld");
}
}
然后进行相关配置:
<mvc:annotation-driven>
<mvc:return-value-handlers>
<bean class="UserHandlers"/>
</mvc:return-value-handlers>
</mvc:annotation-driven>
最终我们再访问testReturnHandlers
的时候,Spring就会通过我们自定义的UserHandlers
来处理返回类型是User的返回值;
6.1.9 mvc:path-matching
子标签
该标签用于控制Spring MVC请求URL的路径匹配,比如说,我们的某个方法配置如下:
@RequestMapping("/world")
public String hello() {
return "hello";
}
默认情况下,Spring MVC会自动匹配所有.*
的请求,就上面这个例子而言,我们请求/world
,/wold.html
,/world.do
都会匹配成功,然后执行相应的操作。如果我们不想按照Spring的这种默认的匹配规则来的话,就可以通过配置该标签来完成我们所需的操作。下面来简单看下该标签下的几个参数:
suffix-pattern
元素:布尔类型,true和false,该元素表示URL匹配的时候是否使用默认的后缀模式匹配全部(.*),默认是true,如果我们配置为false的话,那么就上面例子来说,我们再使用/world.*
类似的就匹配不到了。trailing-slash
元素:布尔类型,true和false,该属性表示匹配的时候是否不考虑斜杠/
,默认是true,也就是不考虑斜杠的存在,那么映射到/users
的方法也就可以匹配/users/
。registered-suffixes-only
元素:布尔类型,true和false,具体作用不明,官网解释:
Whether to use suffix pattern match for registered file extensions only when matching patterns to requests.If enabled, a controller method mapped to "/users" also matches to "/users.json" assuming ".json" is a file extension registered with the provided ContentNegotiationManager. This can be useful for allowing only specific URL extensions to be used as well as in cases where a "." in the URL path can lead to ambiguous interpretation of path variable content, (e.g. given "/users/{user}" and incoming URLs such as "/users/john.j.joe" and "/users/john.j.joe.json").
If enabled, this attribute also enables suffix-pattern. The default value is false.
path-matcher
元素:我们自定义的地址匹配PathMatcher的实现,如果我们不配置,默认的实现是AntPathMatcher
;path-helper
元素:我们自定义的用于解析路径匹配的UrlPathHelper的实现,默认的是UrlPathHelper。
<mvc:annotation-driven>
<mvc:path-matching
suffix-pattern="true"
trailing-slash="false"
registered-suffixes-only="true"
path-helper="pathHelper"
path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
6.1.10 mvc:message-converters
子标签
该标签表示配置Spring的消息转换器。我们都使用过@RequestBody和@ResponseBody这两个注解,当使用RequestBody注解,对请求的数据进行对象映射时,Spring会根据Request对象header部分的content-Type类型,选择合适的HttpMessageConverter来读取数据;而ResponseBody,则是在进行响应时,同样选择合适的HttpMessageConverter来进行转换,返回对应格式的数据。
假如我们通过RequestBody注解定义一个对象,来接收前台传入的json参数时,有的时候可能会由于属性的不匹配出现400错误,这时候,我们就可以自定义HttpMessageConverter来完成我们的参数匹配。具体的实例可参考:SpringMVC自定义配置消息转换器踩坑总结
该标签有一个参数:
register-defaults
,表示除了我们自定义的之外,是否还使用默认的HttpMessageConverter来处理。布尔类型,默认是true,大多数情况下这个值都是true类型。
<mvc:message-converters register-defaults="true">
<!-- 字符串返回时的编码问题 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
<property name="objectMapper"><ref bean="objectMapper"/> </property>
</bean>
</mvc:message-converters>
6.1.11 mvc:async-support
子标签
Spring MVC异步请求的配置选项。一般情况下,在同步请求中,浏览器发起请求,服务器开启一个线程响应,响应完成返回一个值给浏览器,而异步的话返回的一般是Callable对象或者DeferredResult对象,然后后端会通过一个回调线程来处理调用的结果,处理完成后再返回。这里请求线程与后台处理是两个线程,并且Callable对象和DeferredResult对象略有些不同,感兴趣的可以自行搜索一下。
-
default-timeout
属性,默认超时时间,单位毫秒,如果不配置,则使用底层实现的默认超时时间,比如Servlet3中Tomcat上的10秒; -
task-executor
属性,线程池配置,默认是使用SimpleAsyncTaskExecutor线程池来执行Callable的代码,不过该线程池不重用任何线程,或者说它每次调用都启动一个新线程,性能不是太高,我们可以使用自定义线程池来实现。
实现可参考:Spring MVC 异步处理请求,提高程序性能
下面看下该标签下的两个子标签:
mvc:callable-interceptors
,异步操作的拦截器处理,我们可以自定义拦截器来处理异步操作过程中的一些问题,拦截的响应对象是Callable,比如说超时了咋滴整等;
The ordered set of interceptors that intercept the lifecycle of concurrently executed requests, which start after a controller returns a java.util.concurrent.Callable.mvc:deferred-result-interceptors
,和上面这个标签类似,不过该标签拦截的是DeferredResult对象;
The ordered set of interceptors that intercept the lifecycle of concurrently executed requests, which start after a controller returns a DeferredResult.
6.2 mvc:interceptors
标签
Spring MVC中的拦截器的配置,拦截日志,登陆,权限等操作,我们直接来看一下该标签下各元素的使用。
-
path-matcher
元素:表示我们可以自定义拦截器路径的匹配规则,默认采用的匹配规则是AntPathMatcher,具有ant风格的路径规则,如?表示任何单字符,*表示0个或多个字符,**表示0个或多个目录。
6.2.1 mvc:interceptor
子标签
mvc:interceptors
标签下有三种子标签,也就是有两种类型的配置,其中bean和ref表示拦截所有的请求,而mvc:interceptor
则是一种更细粒度的配置,可以自定义拦截的路径和要排除的路径。当有多个拦截器的时候,执行的顺序将按照它们配置的顺序来执行。
- mvc:mapping,表示要拦截的路径,其中属性
path
为具体要拦截的路径,ref或bean表示具体实现的拦截器; - mvc:exclude-mapping,表示要排除掉不需要拦截的路径;
6.3 mvc:default-servlet-handler
标签
Spring MVC用于处理静态资源的一种方式。为了实现优雅的Rest风格的的方式,我们配置DispatcherServlet的请求路径是一般都是配置为/
,这种情况下Spring MVC将会捕获Web容器所有的请求,包括静态资源的请求,然后分发到具体的controller去处理,如果找不到则会出现异常。这种情况下,对于静态资源请求的处理,则显得尤为重要了,而Spring MVC自从3.0之后就提供了两种比较优雅的方式:
- 一种是配置
mvc:default-servlet-handler
,配置之后,Spring MVC上下文中会定义一个DefaultServletHttpRequestHandler,请求进入DispatcherServlet
之后,会被分发到各个HandlerMapping进行处理,如果该请求最后处理不了,会交给默认的DefaultServletHttpRequestHandler
来进行处理;- 一般Web应用服务器默认的Servlet名称是"default",假如我们的Web应用服务器的默认Servlet名称不是"default",则需要通过
default-servlet-name
属性显示指定;
<mvc:default-servlet-handler default-servlet-name="_ah_default"/>
而DefaultServletHttpRequestHandler中各个服务器对应的servlet名称如下:
/** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish */
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
/** Default Servlet name used by Google App Engine */
private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default";
/** Default Servlet name used by Resin */
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
/** Default Servlet name used by WebLogic */
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
/** Default Servlet name used by WebSphere */
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
6.4 mvc:resources
标签
而另一种处理静态资源的方式就是mvc:resources
标签了。mvc:default-servlet-handler
标签将静态资源的处理最终是由Spring MVC交给了Web服务器进行处理,而mvc:resources
则是由Spring MVC自己处理,并且还可以配置一些简单的优化方式。
<mvc:resources mapping="/javascript/" location="/WEB-INF/js/" order="1" cache-period="31536000"/>
location
元素:首先,<mvc:resources/> 允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中,通过location属性指定静态资源的位置,当然支持通配符的方式;cache-period
元素:我们可以通过配置该属性指定静态资源在浏览器端的缓存时间,单位是秒,默认是没有缓存。在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,直接从缓存中取值,提高程序性能;mapping
元素:映射地址,比如当我们要引用/WEB-INF/js/
文件夹下的文件时,可以直接引用/javascript/**.js
,这样就会去我们的WEB-INF/js/
里面去找。也就是说,配置了mapping之后,Spring会做一层映射,然后去映射所对应的location下查找静态资源,因为location的位置不是固定的;order
元素:如果配置了多个mvc:resources,那么可以通过order
元素指定过滤查找时的优先级,order值越小优先级越高,默认的order值是:Integer.MAX_VALUE - 1
。
以上静态资源的问题都是基于我们web.xml中配置的url-pattern
是拦截全部/
的情况。而如果我们的配置是*.do
等相关格式的话,就不会有以上相关的问题了。
6.5 mvc:view-controller
标签
对于WEB-INF目录下的页面,我们知道,由于安全性的问题我们无法直接通过URL访问到,一般都是通过在控制器中使用转发的方式来进行访问。而有一些时候我们如果单纯的只是想访问该目录下的页面,并没有一些逻辑操作的时候,可以通过配置该标签将要访问的URL与相应的视图进行映射,不用再通过控制器就可以解析为对应的视图。
<mvc:view-controller path="/test" view-name="hello/hello"/>
path
参数,是要访问的URL,比如localhost:8080/test
;view-name
参数,要映射的文件的路径,比如某一个jsp的文件路径是WEB-INF/hello/hello.jsp
,那对应的view-name则就是hello/hello
,不过不要忘记配置视图解析器。另外,view-name
的另外一种方式是重定向的方式:<... view-name="redirect:/admin/index">
,有兴趣的可以尝试一下。
总结
到这里,Spring MVC命名空间下的主要标签都学习完了。MVC下的标签针对的是Http请求过程中的各种配置,了解了这些配置,可以让我们在写Web相关的接口的时候更加游刃有余。
本文主要参考资料:官网资料及StackOverFlow网站