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


    目录

    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===
    " + 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();&nbsp;

    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

     

  • 相关阅读:
    (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误
    HTTP KeepAlive模式
    Windows 7 中的 God Mode
    我的开发环境配置经验
    C#格式化数值结果表(格式化字符串)
    我可怜的笔记本电脑
    JetBrains ReSharper 5.x 注册机
    异常处理准则
    调用 Windows 7 中英文混合朗读
    oracle笔记(2010130)
  • 原文地址:https://www.cnblogs.com/coprince/p/5806659.html
Copyright © 2020-2023  润新知