Spring4 Web Enhancement

从Spring4开始,Spring以Servlet3为进行开发,如果用Spring MVC, 测试框架的话需要指定Servlet3兼容的jar包(因为其Mock的对象都是基于Servlet3的). 另外为了方便Rest开发,通过新的@RestController指定在控制器上,这样就不需要在每个@RequestMapping方法上加 @ResponseBody了。 而且添加了一个AsyncRestTemplate ,支持REST客户端的异步无阻塞支持。

一、@RestController

@RestController  
public class UserController {  
    private UserService userService;  
    @Autowired  
    public UserController(UserService userService) {  
        this.userService = userService;  
    }  
    @RequestMapping("/test")  
      public User view() {  
        User user = new User();  
        user.setId(1L);  
        user.setName("haha");  
        return user;  
    }  
  
    @RequestMapping("/test2")  
    public String view2() {  
        return "{\"id\" : 1}";  
    }  
}

二、mvc:annotation-driven配置变化 统一风格;将enableMatrixVariables改为enable-matrix-variables属性; 将ignoreDefaultModelOnRedirect改为ignore-default-model-on-redirect。

三、提供AsyncRestTemplate用于客户端非阻塞异步支持。

服务端:

@RestController  
public class UserController {  

    private UserService userService;  

    @Autowired  
    public UserController(UserService userService) {  
        this.userService = userService;  
    }  

    // 返回Callable
    @RequestMapping("/api")  
    public Callable<User> api() {  
        System.out.println("=====hello");  
        return new Callable<User>() {  
            @Override  
            public User call() throws Exception {  
                Thread.sleep(10L * 1000); //暂停两秒  
                User user = new User();  
                user.setId(1L);  
                user.setName("haha");  
                return user;  
            }  
        };  
    }  
}

客户端:

public static void main(String[] args) {  

    AsyncRestTemplate template = new AsyncRestTemplate();  

    //调用完后立即返回(没有阻塞)  
    ListenableFuture<ResponseEntity<User>> future = template.getForEntity("http://localhost:9080/spring4/api", User.class);  

    //设置异步回调  
    future.addCallback(new ListenableFutureCallback<ResponseEntity<User>>() {  
        @Override  
        public void onSuccess(ResponseEntity<User> result) {  
            System.out.println("======client get result : " + result.getBody());  
        }  
  
        @Override  
        public void onFailure(Throwable t) {  
            System.out.println("======client failure : " + t);  
        }  
    });  
    System.out.println("==no wait");  
}

四、MvcUriComponentsBuilder: 从控制器获取URI信息

真实环境中, 存在两个@RequestMapping(“/{id}”)是错误的。当前只是为了测试。

@Controller  
@RequestMapping("/user")  
public class UserController {  
  
    @RequestMapping("/{id}")  
    public String view(@PathVariable("id") Long id) {  
        return "view";  
    }  
  
    @RequestMapping("/{id}")  
    public A getUser(@PathVariable("id") Long id) {  
        return new A();  
    }  
  
}
import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.*;  

// fromController(..).build().toString()
// fromMethodName(..).build().toString()
// fromMethodCall(on(UserController.class)).getUser(2L).build().toString()

@Test  
public void test() {  
    MockHttpServletRequest req = new MockHttpServletRequest();  
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(req));  
  
    //MvcUriComponentsBuilder类似于ServletUriComponentsBuilder,但是直接从控制器获取  
    //类级别的  
    System.out.println(  
        fromController(UserController.class).build().toString()    
    );  
  
    //方法级别的  
    System.out.println(  
            fromMethodName(UserController.class, "view", 1L).build().toString()  
    );  
  
    //通过Mock方法调用得到  
    System.out.println(  
            fromMethodCall(on(UserController.class).getUser(2L)).build()  
    );  
}

注意:当前MvcUriComponentsBuilder实现有问题,只有JDK环境支持,大家可以复制一份,然后修改: method.getParameterCount() (Java 8才支持) 到method.getParameterTypes().length

Published: July 12 2014