在Spring MVC之前我們需要在Servlet里處理HttpServletRequest參數對象,但這個對象里的屬性都是通用類型的對象(如字符串),處理起來很繁瑣並且容易出錯,而Spring MVC允許我們直接把參數和返回值定義為對象,比如:
先定義一個對象:
@Data public class User { private String id; private String name; }
然后定義一個Spring MVC方法:
@RequestMapping(value = "/create-user", method = RequestMethod.POST) public void createUser(@RequestBody User user) { LOGGER.debug("參數:user = {}", user); }
其中,參數為User類型的對象,@RequestBody注解通知Spring MVC把http請求的body轉換為User對象,並作為參數調用我們寫的createUser方法。
除把參數定義為對象外,返回值也能為對象:
@RequestMapping(value = "/find-user", method = RequestMethod.GET) @ResponseBody public User findUser() { return new User("01", "yw"); }
返回值對象需要加@ResponseBody注解,該注解通知Spring MVC把返回值對象轉換為字符串,並作為http響應消息中的body部分。
除@RequestBody、@ResponseBody注解外,Spring MVC還提供了其它注解,具體請參見相關文檔說明。
Spring MVC是如何進行http消息與對象之間的轉換呢?答案是HttpMessageConverter轉換器接口。
HttpMessageConverter
Spring MVC就像是一個中間人,一邊為http消息(即HttpServletRequest和HttpServletResponse),另一邊是@RequestMapping方法,Spring MVC的使命就是自動把http消息轉換為對象,以及反過來把對象轉換為字符串返回。這一切都依賴於HttpMessageConverter轉換接口。Spring MVC內置了其很多實現,每個實現對應於一種格式的數據轉換,我們可按需配置加載轉換器。如果未配置則Spring MVC進行默認加載,至於默認加載了哪些轉換器請參見WebMvcConfigurationSupport#addDefaultHttpMessageConverters方法,另請參見[Http Message Converters with the Spring Framework](http://www.baeldung.com/spring-httpmessageconverter-rest)。
特別要提醒的是,如果你配置了則Spring MVC將只加載你配置的,如果你未配置則默認加載。因此,不要誤以為你的配置是對默認值的補充。
我們基本上不需要自己去配置轉換器,使用默認配置就足夠了。但如果你有必要則可這樣配置:
@Configuration @EnableWebMvc public class WeMvcConfiguration extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(...); } }
不論你是否配置了,我們最好都檢查一下運行時到底加載了哪些轉換器:
@EventListener public void on(ContextRefreshedEvent event) { RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class); List<?> messageConverters = requestMappingHandlerAdapter.getMessageConverters(); StringBuilder sb = new StringBuilder(); sb.append("Spring共加載了").append(messageConverters.size()).append("個消息轉換器對象:").append(messageConverters.toString()); LOGGER.info(sb.toString()); }
上面的事件監聽器方法將在Spring啟動完畢后打印輸出所有加載的轉換器。你可做兩個試驗,一是自己不配置,二是自己配置,然后分別啟動系統看看到底加載了哪些轉換器。
JSON
如果系統加載了json轉換器你就可以發送json消息了:
curl -X POST -H 'Content-Type: application/json' -d '{"id":"01","name":"yw"}' http://localhost:8080/create-user
如果調用不成功則很可能是系統並未加載json轉換器。Spring MVC的默認配置只當存在jackson-databind時才加載json轉換器。
你可先檢查是否存在jackson-databind依賴項:
mvn dependency:tree | find "jackson-databind"
若不存在則定義之:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
這樣Spring MVC將自動加載json轉換器。
除發送json消息外,你也可以接收json消息:
curl http://localhost:8080/find-user {"id":"01","name":"yw"}
XML
同json一樣,Spring MVC也是利用轉換器來實現發送和接收xml消息。不同的是,Spring MVC只支持jackson-databind一種json轉換器,但支持jaxb和jackson-databind-xml兩種xml轉換器,你可選擇其一使用。
要讓Spring MVC自動配置jackson-databind-xml就需存在該依賴項。你可先檢查是否存在該依賴項:
mvn dependency:tree | find "jackson-databind-xml"
若不存在則定義之:
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
接着你就可以傳xml消息了:
curl -X POST -H 'Content-Type: application/xml' -d '<root><id>01</id><name>yw</name></root>' http://localhost:8080/create-user
強制數據格式
如上,你的create-user方法既可接收xml消息,也可接收json消息,但有時你希望限制數據格式,此時可加consumes參數:
@RequestMapping(value = "/xml-create-user", consumes = {MediaType.APPLICATION_XML_VALUE}, method = RequestMethod.POST) public void xmlCreateUser(@RequestBody User user) { LOGGER.debug("參數:user = {}", user); }
上例表示只接收xml格式的消息。因此若你再發json消息則將失敗。
同樣,你也可以限制返回值為xml:
@RequestMapping(value = "/xml-find-user", method = RequestMethod.GET, produces = {MediaType.APPLICATION_XML_VALUE}) @ResponseBody public User xmlFindUser() { return new User("02", "yw"); }
接着我們就可以接收xml消息了:
curl http://localhost:8080/xml-find-user <User xmlns=""><id>02</id><name>yw</name></xml>
Jackson XML
上面說到,Spring MVC支持jackson-databind-xml作為xml轉換器,並實際測試得到返回的xml:
<User xmlns=""><id>01</id><name>yw</name></xml>
其中有 xmlns="" 。該xmlns並無什么副作用,但如果你很在意一定要去掉它則可增加依賴項:
<dependency> <groupId>com.fasterxml.woodstox</groupId> <artifactId>woodstox-core</artifactId> <version>5.0.2</version> </dependency>
其原因請參見[FasterXML討論](https://github.com/FasterXML/jackson-dataformat-xml/issues/32)。
面向對象
上面我們花了很多篇幅介紹了json和xml,但Spring MVC給我們帶來的好處卻在於讓我們忽視它們的存在,讓我們只關注對象,無需去考慮對象是從json來的還是從xml來的,這些底層的事就交給Spring MVC去處理吧。同時,直接把我們自己的對象作為參數和返回值也使得單元測試更容易寫。
練習
1、如果你的系統同時有json和xml轉換器,請解釋為何用RestTemplate代碼和curl工具調用同一個Spring MVC方法時時,一個返回的是xml,另一個返回的是json?
@Test public void test() { RestTemplate restTemplate = new RestTemplate(); String s = restTemplate.getForObject("http://localhost:8080/find-user", String.class); System.out.println(s); // <User xmlns=""><id>01</id><name>yw</name></xml> }
curl http://localhost:8080/find-user {"id":"01","name":"yw"}
2、本說明文檔介紹了用Jackson-Xml作為Xml轉換器,同時說到你也可以用jaxb進行Xml轉換。請改為使用jaxb方式。