Spring4.1新特性——Spring MVC增强

标签: spring4 spring mvc | 发表时间:2014-08-22 00:33 | 作者:
出处:http://www.iteye.com

目录

Spring4.1新特性——综述

Spring4.1新特性——Spring核心部分及其他

Spring4.1新特性——Spring缓存框架增强

Spring4.1新特性——异步调用和事件机制的异常处理

Spring4.1新特性——数据库集成测试脚本初始化

Spring4.1新特性——Spring MVC增强

Spring4.1新特性——页面自动化测试框架Spring MVC Test HtmlUnit简介

Spring4.1新特性——静态资源处理增强

 

Spring 4.1对Spring MVC部分做的增强是最多的,提供了一些视图解析器的mvc标签实现简化配置、提供了GroovyWebApplicationContext用于Groovy web集成、提供了Gson、protobuf的HttpMessageConverter、提供了对groovy-templates模板的支持、JSONP的支持、对Jackson的@JsonView的支持等。

 

1、GroovyWebApplicationContext 

在Spring 4.1之前没有提供Web集成的ApplicationContext,在《 Spring4新特性——Groovy Bean定义DSL》中我们自己去实现的com.sishuok.spring4.context.support.WebGenricGroovyApplicationContext,而4.1其已经提供了相应实现,直接把《 Spring4新特性——Groovy Bean定义DSL》配置中的相应类改掉即可。

 

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"/>
    </bean>

而现在我们可以使用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:velocity-configurer resource-loader-path="/WEB-INF/vm/,classpath:com/github/zhangkaitao"/>
    <mvc:groovy-configurer resource-loader-path="classpath:templates/" cache-templates="false"/>
    <mvc:view-resolvers>
        <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:velocity cache-views="false" prefix="" suffix=".vm"/>
        <mvc:groovy cache-views="false" suffix=".tpl"/>
    </mvc:view-resolvers>

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引擎集成

Spring 4.1提供了对 Groovy Template模板引擎的集成,其是一种DSL风格的模板引擎,其也是最早在Spring Boot中引入的。

4.1、Spring配置文件    

    <mvc:groovy-configurer resource-loader-path="classpath:templates/" cache-templates="false"/>
    <mvc:view-resolvers>
        <mvc:groovy cache-views="false" suffix=".tpl"/>
    </mvc:view-resolvers>

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() {
        return new User(1L, "zhangsan");
    }

    @RequestMapping("/jackson3")
    @JsonView(User.AllView.class) //可以省略
    public User test3() {
        return new User(1L, "zhangsan");
    }
}

使用@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>
    </bean>

然后访问如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响应。 

 

@ContollerAdvice的作用请参考《 Spring3.2新注解@ControllerAdvice》,basePackages用于指定对哪些包里的Controller起作用。

 

6.3、ResponseBodyAdvice

我们之前实现的JsonpAdvice其继承自AbstractJsonpResponseBodyAdvice,而AbstractJsonpResponseBodyAdvice继承自ResponseBodyAdvice,其作用是在响应体写出之前做一些处理: 

@Order(1)
@ControllerAdvice(basePackages = "com.github")
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.getMethod().getReturnType().isAssignableFrom(User.class);
    }

    @Override
    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配置 

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter">
                <constructor-arg>
                    <bean class="com.github.zhangkaitao.web.controller.MyExtensionRegistryInitializer"/>
                </constructor-arg>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

 

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、测试控制器 

@RestController
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());
    }

    @Test
    public void testWrite() {
        UserProtos.User user = UserProtos.User.newBuilder().setId(1).setName("zhangsan").build();
        HttpHeaders headers = new HttpHeaders();
        RequestEntity<UserProtos.User> requestEntity =
                new RequestEntity<UserProtos.User>(user, headers, HttpMethod.POST, URI.create(baseUri + "/proto/write"));

        ResponseEntity<UserProtos.User> responseEntity =
                restTemplate.exchange(requestEntity, UserProtos.User.class);
        System.out.println(responseEntity.getBody());
    }

测试用例知识请参考《 Spring MVC测试框架详解——服务端测试》和《 Spring MVC测试框架详解——客户端测试》。

测试过程中会抛出:

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

其作用可以参考《 Spring4新特性——注解、脚本、任务、MVC等其他特性改进》,Spring 4.1又提供了一个新的方法MvcUriComponentsBuilder.fromMappingName用于根据控制器方法来生成请求URI。

 

@RestController
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()}

如上方式只能在正常EL 3.0的容器中运行,可参考《 Expression Language 3.0新特性》。 

 

11、MockRestServiceServer

MockRestServiceServer目前提供了对AsyncRestTemplate的支持,使用方式和RestTemplate一样。可参考《 Spring MVC测试框架详解——客户端测试》。

 

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());
            }
            @Override
            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://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/markup-template-engine.html

https://spring.io/blog/2014/05/28/using-the-innovative-groovy-template-engine-in-spring-boot

Spring4新特性——Groovy Bean定义DSL

Spring3.2新注解@ControllerAdvice

Spring MVC测试框架详解——服务端测试

Spring MVC测试框架详解——客户端测试

Spring4新特性——注解、脚本、任务、MVC等其他特性改进

 

Spring4新特性

Spring4新特性——泛型限定式依赖注入

Spring4新特性——核心容器的其他改进

Spring4新特性——Web开发的增强

Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC 

Spring4新特性——Groovy Bean定义DSL

Spring4新特性——更好的Java泛型操作API 

Spring4新特性——JSR310日期API的支持

Spring4新特性——注解、脚本、任务、MVC等其他特性改进 

 

源码下载

https://github.com/zhangkaitao/spring4-1-showcase/tree/master/spring4.1-groovy

https://github.com/zhangkaitao/spring4-1-showcase/tree/master/spring4.1-mvc

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring4 spring mvc] 推荐:

Spring MVC 和 Struts2

- - CSDN博客架构设计推荐文章
Web层面的框架学习了三个Struts1和2,SpringMVC,那他们之间肯定存在一个优劣和适用的环境,Struts1和2的异同点我已经做过对比《 Struts1和Struts2》,这篇将对比下Struts2和SpringMVC的异同,下面数据基本来源于网络,本人是搜集整理所得,供大家参考. 一个项目使用什么样的技术,决定的因素很多,我所能想到的有:对系统的性能、开发的效率、团队学习的成本、业务场景等,下面尽量从这几个方面入手,来分析比较下他们之间存在的优劣.

Spring MVC 3 深入总结

- - 企业架构 - ITeye博客
大家好,Spring3 MVC是非常优秀的MVC框架,由其是在3.0版本发布后,现在有越来越多的团队选择了Spring3 MVC了. Spring3 MVC结构简单,应了那句话简单就是美,而且他强大不失灵活,性能也很优秀. 官方的下载网址是: http://www.springsource.org/download   (本文使用是的Spring 3.0.5版本).

Spring MVC 与 web开发

- - 码蜂笔记
项目组用了 Spring MVC 进行开发,觉得对里面的使用方式不是很满意,就想,如果是我来搭建开发环境,我会怎么做. 下面就是我的想法,只关注于 MVC 的 View 层. 现在基本上都是用 ajax 来调用后台接口,拿到 json格式的数据再展示,有的人直接返回数据,却没有考虑异常的情况,我觉得返回的报文里必须包含表示可能的异常信息的数据和业务响应数据.

Spring MVC的常见错误

- - Java译站
10年前我开始自己的职业生涯的时候,Struts还是市场上的主流标准. 然而多年过后,我发现Spring MVC已经越来越流行了. 对我而言这并不意外,因为它能和Spring容器无缝集成,同时它还提供了灵活性及扩展性. 从我迄今为止对Spring的经验来看,我发现有不少人在配置Spring的时候经常会犯一些常见的错误.

spring mvc 异常处理(转)

- - 编程语言 - ITeye博客
链接:http://gaojiewyh.iteye.com/blog/1297746 (附源码). 链接:http://zywang.iteye.com/blog/983801 . 链接:http://www.cnblogs.com/xguo/p/3163519.html . 链接:http://fuliang.iteye.com/blog/947191 .

Spring MVC 入门实例

- - CSDN博客推荐文章
springmvc 框架围绕DispatcherServlet这个核心展开,DispatcherServlet是Spring MVC的总控制,它负责截获请求并将其分派给相应的处理器处理. SpringMVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理以及表单标签绑定等内容.

Spring MVC Controller单例陷阱

- - 企业架构 - ITeye博客
Spring MVC Controller单例陷阱. 标签:Spring mvc. 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明. Spring MVC Controller默认是单例的:. 1、这个不用废话了,单例不用每次都new,当然快了. 2、不需要实例会让很多人迷惑,因为spring mvc官方也没明确说不可以多例.

Spring MVC 中 HandlerInterceptorAdapter的使用

- - 企业架构 - ITeye博客
一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理. Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度. Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器.

Spring MVC防重复提交

- - 企业架构 - ITeye博客
如何在Spring MVC里面解决此问题(其它框架也一样,逻辑一样,思想一样,和具体框架没什么关系). 要解决重复提交,有很多办法,比如说在提交完成后redirect一下,也可以用本文提到的使用token的方法(我不使用redirect是因为那样解决不了ajax提交数据或者移动应用提交数据,另一个原因是现在比较通行的方法是使用token,像python里的django框架也是使用token来解决).

Spring MVC 3.2.4 ResponseBody 编码问题解决

- - 编程语言 - ITeye博客
首先请确保Spring版本为3.2.4. 问题1:使用@ResponseBody注解,返回对象类型时,如Map,中文字符,在客户端会显示为???. 解决办法:请检查依赖jar包,确保spring-context-support.jar的版本也是3.2.4,则可显示中文;. 问题2:使用@ResponseBody注解,返回String时,中文字符,在客户端会显示为???,并且contextType中会缺失encoding值,即为text/html但是,没有后面的encode.