spring 4.1對spring mvc部分做的增強是最多的,提供了一些視圖解析器的mvc标簽實作簡化配置、提供了groovywebapplicationcontext用于groovy web內建、提供了gson、protobuf的httpmessageconverter、提供了對groovy-templates模闆的支援、jsonp的支援、對jackson的@jsonview的支援等。
1、groovywebapplicationcontext
2、視圖解析器标簽
之前我們都是這樣定義視圖解析器:

<bean id="mvcvelocityengine" class="org.springframework.web.servlet.view.velocity.velocityconfigurer">
<property name="resourceloaderpath" value="/web-inf/vm/,classpath:com/github/zhangkaitao" />
</bean>
<bean id="viewresolver" class="org.springframework.web.servlet.view.velocity.velocityviewresolver">
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
<property name="cache" value="false"/>
而現在我們可以使用mvc标簽定義:

<mvc:velocity-configurer resource-loader-path="/web-inf/vm/,classpath:com/github/zhangkaitao"/>
<mvc:view-resolvers>
<mvc:velocity cache-views="false" prefix="" suffix=".vm"/>
</mvc:view-resolvers>
再來看一個更複雜的例子:

<mvc:groovy-configurer resource-loader-path="classpath:templates/" cache-templates="false"/>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.mappingjackson2jsonview">
<property name="jsonpparameternames">
<set>
<value>jsonp</value>
<value>callback</value>
</set>
</property>
</bean>
</mvc:default-views>
</mvc:content-negotiation>
<mvc:groovy cache-views="false" suffix=".tpl"/>
mvc:content-negotiation用于定義内容協商的視圖解析器,且内部可以定義預設視圖;然後我們又定義了mvc:velocity和mvc:groovy兩個視圖解析器;它們會按照順序進行解析。另外幾個視圖解析器是:
mvc:freemarker
mvc:bean-name
mvc:jsp
這種方式有一個很大的問題就是隻能做預設配置,如果想自定義其屬性值就搞不定了,估計當時開發的人考慮不全或沒有經驗。
3、控制器标簽
spring 4.1提供了更豐富的控制器标簽:
3.1、重定向視圖控制器标簽

<mvc:redirect-view-controller
path="/redirect"
redirect-url="/status"
context-relative="true"
status-code="301"
keep-query-params="true"/>
3.2、狀态控制器标簽

<mvc:status-controller path="/status" status-code="200"/>
3.3、帶狀态的視圖控制器标簽

<mvc:view-controller path="/error/**" status-code="200"/>
4、groovy template引擎內建
4.1、spring配置檔案

4.2、模闆heelo.tpl

yieldunescaped '<!doctype html>'
html {
head {
title('hello groovy templates')
}
body {
div("hello $user.name")
}
具體文法請參考官方文檔。
5、 jackson @jsonview支援
可以使用@jsonview來分組渲染json資料,按需展示json資料。
5.1、模型

public class user implements serializable {
public static interface onlyidview {}
public static interface onlynameview {}
public static interface allview extends onlyidview, onlynameview {}
@jsonview(onlyidview.class)
private long id;
@jsonview(onlynameview.class)
private string name;
……
定義了三個視圖:onlyidview、onlynameview和allview。
5.2、控制器

@restcontroller
public class jacksonjsonviewcontroller {
@requestmapping("/jackson1")
@jsonview(user.onlyidview.class)
public user test1() {
return new user(1l, "zhangsan");
}
@requestmapping("/jackson2")
@jsonview(user.onlynameview.class)
public user test2() {
@requestmapping("/jackson3")
@jsonview(user.allview.class) //可以省略
public user test3() {
使用@jsonview控制渲染哪些資料。
6、jsonp支援
6.1、mappingjackson2jsonview提供的支援

<bean class="org.springframework.web.servlet.view.json.mappingjackson2jsonview">
<property name="jsonpparameternames">
<set>
<value>jsonp</value>
<value>callback</value>
</set>
</property>
然後通路如http://localhost:8080/json?callback=callback即可得到jsonp響應:callback({"user":{"id":1,"name":"zhangsan"}});。
6.2、對使用httpmessageconverter的@responsebody的支援

@order(2)
@controlleradvice(basepackages = "com.github")
public class jsonpadvice extends abstractjsonpresponsebodyadvice {
public jsonpadvice() {
super("callback", "jsonp"); //指定jsonpparameternames
通路http://localhost:8080/jackson1?callback=callback即可看到jsonp響應。
6.3、responsebodyadvice
我們之前實作的jsonpadvice其繼承自abstractjsonpresponsebodyadvice,而abstractjsonpresponsebodyadvice繼承自responsebodyadvice,其作用是在響應體寫出之前做一些處理:

@order(1)
public class myresponsebodyadvice implements responsebodyadvice<object> {
@override
public boolean supports(methodparameter methodparameter, class<? extends httpmessageconverter<?>> convertertype) {
return methodparameter.getmethod().getreturntype().isassignablefrom(user.class);
public object beforebodywrite(
object obj, methodparameter methodparameter, mediatype mediatype,
class<? extends httpmessageconverter<?>> convertertype,
serverhttprequest serverhttprequest, serverhttpresponse serverhttpresponse) {
user user = ((user)obj);
user.setname("---" + user.getname() + "---");
return user;
1、supports指定支援哪些類型的方法進行處理,此處是傳回值為user的;2、我們得到user對象然後在名字前後拼上”---“ ;3、可以指定多個responsebodyadvice,使用@order指定順序。通路http://localhost:8080/jackson2?callback=callback可以看到效果。
7、gson httpmessageconverter
7.1、spring配置

<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.gsonhttpmessageconverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
使用方式和jackson json類似。本文使用的是<gson.version>2.2.4</gson.version>版本。
8、protobuf httpmessageconverter
8.1、spring配置

<bean class="org.springframework.http.converter.protobuf.protobufhttpmessageconverter">
<constructor-arg>
<bean class="com.github.zhangkaitao.web.controller.myextensionregistryinitializer"/>
</constructor-arg>
</bean>
8.2、定義protobuf message(proto/user.proto)

package com.github.zhangkaitao.pb;
option java_package = "com.github.zhangkaitao.pb";
option java_outer_classname = "userprotos";
message user {
optional int64 id = 1;
optional string name = 2;
}
8.3、添加maven插件自動把protobuf message轉化成java代碼

<plugin>
<groupid>com.google.protobuf.tools</groupid>
<artifactid>maven-protoc-plugin</artifactid>
<version>0.1.10</version>
<executions>
<execution>
<id>generate-sources</id>
<goals>
<goal>compile</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<protosourceroot>${basedir}/src/main/proto/</protosourceroot>
<includes>
<param>**/*.proto</param>
</includes>
</configuration>
</execution>
</executions>
<configuration>
<protocexecutable>d:/software/protoc.exe</protocexecutable>
</configuration>
</plugin>
8.4、測試控制器

public class protobufcontroller {
@requestmapping("/proto/read")
public responseentity<userprotos.user> protoread() {
return responseentity.ok(userprotos.user.newbuilder().setid(1).setname("zhangsan").build());
@requestmapping("/proto/write")
public responseentity<userprotos.user> protoread(requestentity<userprotos.user> requestentity) {
system.out.println("server===\n" + requestentity.getbody());
return responseentity.ok(requestentity.getbody());
8.5、測試用例(com.github.zhangkaitao.proto.prototest)

@test
public void testread() {
httpheaders headers = new httpheaders();
requestentity<userprotos.user> requestentity =
new requestentity<userprotos.user>(headers, httpmethod.post, uri.create(baseuri + "/proto/read"));
responseentity<userprotos.user> responseentity =
resttemplate.exchange(requestentity, userprotos.user.class);
system.out.println(responseentity.getbody());
public void testwrite() {
userprotos.user user = userprotos.user.newbuilder().setid(1).setname("zhangsan").build();
new requestentity<userprotos.user>(user, headers, httpmethod.post, uri.create(baseuri + "/proto/write"));
測試過程中會抛出:

caused by: java.lang.unsupportedoperationexception
at java.util.collections$unmodifiablemap.put(collections.java:1342)
at org.springframework.http.httpheaders.set(httpheaders.java:869)
at org.springframework.http.converter.protobuf.protobufhttpmessageconverter.setprotoheader(protobufhttpmessageconverter.java:196)
這是因為protobufhttpmessageconverter會修改響應頭,但是responseentity構造時httpheaders是不允許修改的。暫時解決辦法是注釋掉:

//setprotoheader(outputmessage, message);
9、requestentity/responseentity
spring 4.1提供了responseentity配對的requestentity,使用方式和httpentity一樣。具體可以參考com.github.zhangkaitao.web.controller.requestresponseentitycontroller。
10、mvcuricomponentsbuilder

public class mvcuricomponentsbuildercontroller {
@requestmapping("/uri")
public string mvcuricomponentsbuilder1() {
return mvcuricomponentsbuilder.frommappingname("mucbc#mvcuricomponentsbuilder1").build();
@requestmapping("/uri/{id}")
public string mvcuricomponentsbuilder2(@pathvariable long id) {
return mvcuricomponentsbuilder.frommappingname("mucbc#mvcuricomponentsbuilder2").arg(0, "123").build();
規則是“控制器所有大寫字母#方法名”找到相應的方法。 另外可以直接在頁面中使用如下方式擷取相應的uri:

${s:mvcurl('mucbc#mvcuricomponentsbuilder2').arg(0,"123").build()}
11、mockrestserviceserver
12、mockmvcconfigurer
spring 4.1提供了mockmvcconfigurer用于進行一些通用配置,使用方式如下:

mockmvc = mockmvcbuilders.webappcontextsetup(context).apply(defaultsetup()).build();
mockmvcconfigurer實作:

private mockmvcconfigurer defaultsetup() {
return new mockmvcconfigurer() {
@override
public void afterconfigureradded(configurablemockmvcbuilder<?> configurablemockmvcbuilder) {
configurablemockmvcbuilder.alwaysexpect(status().isok());
}
public requestpostprocessor beforemockmvccreated(configurablemockmvcbuilder<?> configurablemockmvcbuilder, webapplicationcontext webapplicationcontext) {
return new requestpostprocessor() {
@override
public mockhttpservletrequest postprocessrequest(mockhttpservletrequest mockhttpservletrequest) {
mockhttpservletrequest.setattribute("aa", "aa");
return mockhttpservletrequest;
}
};
};
可以在如上實作中進行一些通用配置,如安全(往request中扔安全對象之類的)。測試用例可參考com.github.zhangkaitao.proto.prototest2。
本文轉自http://jinnianshilongnian.iteye.com/blog/2107205