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

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

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、视图解析器标签

之前我们都是这样定义视图解析器:

Java代码   收藏代码
  1. <bean id="mvcVelocityEngine" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">  
  2.     <property name="resourceLoaderPath" value="/WEB-INF/vm/,classpath:com/github/zhangkaitao" />  
  3. </bean>  
  4. <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">  
  5.     <property name="prefix" value=""/>  
  6.     <property name="suffix" value=".vm"/>  
  7.     <property name="cache" value="false"/>  
  8. </bean>  

而现在我们可以使用MVC标签定义: 

Java代码   收藏代码
  1. <mvc:velocity-configurer resource-loader-path="/WEB-INF/vm/,classpath:com/github/zhangkaitao"/>  
  2. <mvc:view-resolvers>  
  3.     <mvc:velocity cache-views="false" prefix="" suffix=".vm"/>  
  4. </mvc:view-resolvers>  

 

再来看一个更复杂的例子: 

Java代码   收藏代码
  1. <mvc:velocity-configurer resource-loader-path="/WEB-INF/vm/,classpath:com/github/zhangkaitao"/>  
  2. <mvc:groovy-configurer resource-loader-path="classpath:templates/" cache-templates="false"/>  
  3. <mvc:view-resolvers>  
  4.     <mvc:content-negotiation>  
  5.         <mvc:default-views>  
  6.             <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">  
  7.                 <property name="jsonpParameterNames">  
  8.                     <set>  
  9.                         <value>jsonp</value>  
  10.                         <value>callback</value>  
  11.                     </set>  
  12.                 </property>  
  13.             </bean>  
  14.         </mvc:default-views>  
  15.     </mvc:content-negotiation>  
  16.     <mvc:velocity cache-views="false" prefix="" suffix=".vm"/>  
  17.     <mvc:groovy cache-views="false" suffix=".tpl"/>  
  18. </mvc:view-resolvers>  

mvc:content-negotiation用于定义内容协商的视图解析器,且内部可以定义默认视图;然后我们又定义了mvc:velocity和mvc:groovy两个视图解析器;它们会按照顺序进行解析。另外几个视图解析器是:

 

mvc:freemarker

mvc:bean-name

mvc:jsp

 

这种方式有一个很大的问题就是只能做默认配置,如果想自定义其属性值就搞不定了,估计当时开发的人考虑不全或没有经验。

 

3、控制器标签

Spring 4.1提供了更丰富的控制器标签:

3.1、重定向视图控制器标签

Java代码   收藏代码
  1. <mvc:redirect-view-controller  
  2.         path="/redirect"  
  3.         redirect-url="/status"  
  4.         context-relative="true"  
  5.         status-code="301"  
  6.         keep-query-params="true"/>  

3.2、状态控制器标签

Java代码   收藏代码
  1. <mvc:status-controller path="/status" status-code="200"/>  

3.3、带状态的视图控制器标签

Java代码   收藏代码
  1. <mvc:view-controller path="/error/**" status-code="200"/>  

   

4、Groovy Template引擎集成

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

4.1、Spring配置文件    

Java代码   收藏代码
  1. <mvc:groovy-configurer resource-loader-path="classpath:templates/" cache-templates="false"/>  
  2. <mvc:view-resolvers>  
  3.     <mvc:groovy cache-views="false" suffix=".tpl"/>  
  4. </mvc:view-resolvers>  

4.2、模板heelo.tpl

Java代码   收藏代码
  1. yieldUnescaped '<!DOCTYPE html>'  
  2. html {  
  3.   head {  
  4.     title('hello groovy templates')  
  5.   }  
  6.   body {  
  7.       div("hello $user.name")  
  8.   }  
  9. }  

具体语法请参考官方文档。

 

5、 Jackson @JsonView支持 

可以使用@JsonView来分组渲染JSON数据,按需展示JSON数据。

5.1、模型

Java代码   收藏代码
  1. public class User implements Serializable {  
  2.     public static interface OnlyIdView {}  
  3.     public static interface OnlyNameView {}  
  4.     public static interface AllView extends OnlyIdView, OnlyNameView {}  
  5.   
  6.     @JsonView(OnlyIdView.class)  
  7.     private Long id;  
  8.   
  9.     @JsonView(OnlyNameView.class)  
  10.     private String name;    
  11.     ……  
  12. }  

定义了三个视图:OnlyIdView、OnlyNameView和AllView。

 

5.2、控制器

Java代码   收藏代码
  1. @RestController  
  2. public class JacksonJsonViewController {  
  3.   
  4.     @RequestMapping("/jackson1")  
  5.     @JsonView(User.OnlyIdView.class)  
  6.     public User test1() {  
  7.         return new User(1L, "zhangsan");  
  8.     }  
  9.   
  10.     @RequestMapping("/jackson2")  
  11.     @JsonView(User.OnlyNameView.class)  
  12.     public User test2() {  
  13.         return new User(1L, "zhangsan");  
  14.     }  
  15.   
  16.     @RequestMapping("/jackson3")  
  17.     @JsonView(User.AllView.class//可以省略  
  18.     public User test3() {  
  19.         return new User(1L, "zhangsan");  
  20.     }  
  21. }  

使用@JsonView控制渲染哪些数据。

 

6、Jsonp支持  

6.1、MappingJackson2JsonView提供的支持 

Java代码   收藏代码
  1. <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">  
  2.     <property name="jsonpParameterNames">  
  3.         <set>  
  4.             <value>jsonp</value>  
  5.             <value>callback</value>  
  6.         </set>  
  7.    </property>  
  8. </bean>  

然后访问如http://localhost:8080/json?callback=callback即可得到JSONP响应:callback({"user":{"id":1,"name":"zhangsan"}});。

 

6.2、对使用HttpMessageConverter的@ResponseBody的支持 

Java代码   收藏代码
  1. @Order(2)  
  2. @ControllerAdvice(basePackages = "com.github")  
  3. public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {  
  4.     public JsonpAdvice() {  
  5.         super("callback""jsonp"); //指定jsonpParameterNames  
  6.     }  
  7. }  

访问http://localhost:8080/jackson1?callback=callback即可看到JSONP响应。 

 

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

 

6.3、ResponseBodyAdvice

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

Java代码   收藏代码
  1. @Order(1)  
  2. @ControllerAdvice(basePackages = "com.github")  
  3. public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {  
  4.   
  5.     @Override  
  6.     public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {  
  7.         return methodParameter.getMethod().getReturnType().isAssignableFrom(User.class);  
  8.     }  
  9.   
  10.     @Override  
  11.     public Object beforeBodyWrite(  
  12.             Object obj, MethodParameter methodParameter, MediaType mediaType,  
  13.             Class<? extends HttpMessageConverter<?>> converterType,  
  14.             ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {  
  15.   
  16.         User user = ((User)obj);  
  17.         user.setName("---" + user.getName() + "---");  
  18.         return user;  
  19.     }  
  20. }  

1、supports指定支持哪些类型的方法进行处理,此处是返回值为User的;2、我们得到User对象然后在名字前后拼上”---“ ;3、可以指定多个ResponseBodyAdvice,使用@Order指定顺序。访问http://localhost:8080/jackson2?callback=callback可以看到效果。

 

7、Gson HttpMessageConverter

7.1、Spring配置 

Java代码   收藏代码
  1. <mvc:annotation-driven>  
  2.     <mvc:message-converters>  
  3.         <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"/>  
  4.     </mvc:message-converters>  
  5. </mvc:annotation-driven>  

使用方式和Jackson Json类似。本文使用的是<gson.version>2.2.4</gson.version>版本。

 

8、Protobuf HttpMessageConverter

8.1、Spring配置 

Java代码   收藏代码
  1. <mvc:annotation-driven>  
  2.     <mvc:message-converters>  
  3.         <bean class="org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter">  
  4.             <constructor-arg>  
  5.                 <bean class="com.github.zhangkaitao.web.controller.MyExtensionRegistryInitializer"/>  
  6.             </constructor-arg>  
  7.         </bean>  
  8.     </mvc:message-converters>  
  9. </mvc:annotation-driven>  

 

8.2、定义protobuf message(proto/user.proto)

Java代码   收藏代码
  1. package com.github.zhangkaitao.pb;  
  2.    
  3.  option java_package = "com.github.zhangkaitao.pb";  
  4.  option java_outer_classname = "UserProtos";  
  5.    
  6.  message User {  
  7.    optional int64 id = 1;  
  8.    optional string name = 2;  
  9.  }  

 

8.3、添加maven插件自动把protobuf message转化成Java代码

Java代码   收藏代码
  1. <plugin>  
  2.     <groupId>com.google.protobuf.tools</groupId>  
  3.     <artifactId>maven-protoc-plugin</artifactId>  
  4.     <version>0.1.10</version>  
  5.     <executions>  
  6.         <execution>  
  7.             <id>generate-sources</id>  
  8.             <goals>  
  9.                 <goal>compile</goal>  
  10.             </goals>  
  11.             <phase>generate-sources</phase>  
  12.             <configuration>  
  13.                 <protoSourceRoot>${basedir}/src/main/proto/</protoSourceRoot>  
  14.                 <includes>  
  15.                     <param>**/*.proto</param>  
  16.                 </includes>  
  17.             </configuration>  
  18.         </execution>  
  19.     </executions>  
  20.     <configuration>  
  21.         <protocExecutable>D:/software/protoc.exe</protocExecutable>  
  22.     </configuration>  
  23. </plugin>  

 

8.4、测试控制器 

Java代码   收藏代码
  1. @RestController  
  2. public class ProtobufController {  
  3.     @RequestMapping("/proto/read")  
  4.     public ResponseEntity<UserProtos.User> protoRead() {  
  5.         return ResponseEntity.ok(UserProtos.User.newBuilder().setId(1).setName("zhangsan").build());  
  6.     }  
  7.     @RequestMapping("/proto/write")  
  8.     public ResponseEntity<UserProtos.User> protoRead(RequestEntity<UserProtos.User> requestEntity) {  
  9.         System.out.println("server===\n" + requestEntity.getBody());  
  10.         return ResponseEntity.ok(requestEntity.getBody());  
  11.     }  
  12. }  

 

8.5、测试用例(com.github.zhangkaitao.proto.ProtoTest)   

Java代码   收藏代码
  1. @Test  
  2. public void testRead() {  
  3.     HttpHeaders headers = new HttpHeaders();  
  4.     RequestEntity<UserProtos.User> requestEntity =  
  5.             new RequestEntity<UserProtos.User>(headers, HttpMethod.POST, URI.create(baseUri + "/proto/read"));  
  6.   
  7.     ResponseEntity<UserProtos.User> responseEntity =  
  8.             restTemplate.exchange(requestEntity, UserProtos.User.class);  
  9.   
  10.     System.out.println(responseEntity.getBody());  
  11. }  
  12.   
  13. @Test  
  14. public void testWrite() {  
  15.     UserProtos.User user = UserProtos.User.newBuilder().setId(1).setName("zhangsan").build();  
  16.     HttpHeaders headers = new HttpHeaders();  
  17.     RequestEntity<UserProtos.User> requestEntity =  
  18.             new RequestEntity<UserProtos.User>(user, headers, HttpMethod.POST, URI.create(baseUri + "/proto/write"));  
  19.   
  20.     ResponseEntity<UserProtos.User> responseEntity =  
  21.             restTemplate.exchange(requestEntity, UserProtos.User.class);  
  22.     System.out.println(responseEntity.getBody());  
  23. }  

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

测试过程中会抛出:

Java代码   收藏代码
  1. Caused by: java.lang.UnsupportedOperationException  
  2.     at java.util.Collections$UnmodifiableMap.put(Collections.java:1342)  
  3.     at org.springframework.http.HttpHeaders.set(HttpHeaders.java:869)  
  4.     at org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter.setProtoHeader(ProtobufHttpMessageConverter.java:196)  

这是因为ProtobufHttpMessageConverter会修改响应头,但是ResponseEntity构造时HttpHeaders是不允许修改的。暂时解决办法是注释掉:

Java代码   收藏代码
  1. //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。

 

Java代码   收藏代码
  1. @RestController  
  2. public class MvcUriComponentsBuilderController {  
  3.   
  4.     @RequestMapping("/uri")  
  5.     public String mvcUriComponentsBuilder1() {  
  6.         return MvcUriComponentsBuilder.fromMappingName("MUCBC#mvcUriComponentsBuilder1").build();  
  7.     }  
  8.     @RequestMapping("/uri/{id}")  
  9.     public String mvcUriComponentsBuilder2(@PathVariable Long id) {  
  10.         return MvcUriComponentsBuilder.fromMappingName("MUCBC#mvcUriComponentsBuilder2").arg(0"123").build();  
  11.     }  
  12. }  

规则是“控制器所有大写字母#方法名”找到相应的方法。 另外可以直接在页面中使用如下方式获取相应的URI:

Java代码   收藏代码
  1. ${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用于进行一些通用配置,使用方式如下:

Java代码   收藏代码
  1. mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(defaultSetup()).build();   

MockMvcConfigurer实现: 

Java代码   收藏代码
  1. private MockMvcConfigurer defaultSetup() {  
  2.     return new MockMvcConfigurer() {  
  3.         @Override  
  4.         public void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> configurableMockMvcBuilder) {  
  5.             configurableMockMvcBuilder.alwaysExpect(status().isOk());  
  6.         }  
  7.         @Override  
  8.         public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> configurableMockMvcBuilder, WebApplicationContext webApplicationContext) {  
  9.             return new RequestPostProcessor() {  
  10.                 @Override  
  11.                 public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mockHttpServletRequest) {  
  12.                     mockHttpServletRequest.setAttribute("aa""aa");  
  13.                     return mockHttpServletRequest;  
  14.                 }  
  15.             };  
  16.         }  
  17.     };  
  18. }  

可以在如上实现中进行一些通用配置,如安全(往Request中扔安全对象之类的)。测试用例可参考com.github.zhangkaitao.proto.ProtoTest2。


本文转自http://jinnianshilongnian.iteye.com/blog/2107205

目录
相关文章
|
7月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
856 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
负载均衡 Java API
Spring Cloud是什么及基本特性都有哪些?
Spring Cloud 是用于构建健壮云应用的框架,包含多个子项目。其核心组件如Eureka(服务注册与发现)、Hystrix(熔断器)、Ribbon(负载均衡)等,帮助开发者快速实现微服务架构。Spring Cloud 提供了服务注册与发现、分布式配置、路由、断路器等功能,简化了微服务开发与管理。本文将重点介绍服务注册与发现及分布式配置两大特性。
833 5
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
1541 0
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
905 0
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
861 0
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
580 0
|
10月前
|
Java 数据库连接 API
Java 8 + 特性及 Spring Boot 与 Hibernate 等最新技术的实操内容详解
本内容涵盖Java 8+核心语法、Spring Boot与Hibernate实操,按考试考点分类整理,含技术详解与代码示例,助力掌握最新Java技术与应用。
311 2
|
10月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
732 0
|
10月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
315 0