零XML配置 SpringMVC 進階之路
上一篇文章我們搭建好了基礎環境,寫了一個簡單的controller。
使用RestController寫一個簡單的介面,返回了一串”helloWorld”字串,現在我們改下controller的內容:
@RestController
@RequestMapping("/api")
public class TestController {
@GetMapping("/hello")
public Object hello(String name){
System.out.println(name);
return "你好, " + name;
}
}
瀏覽器中輸入:
http://localhost:8080/api/hello?name=中國人
控制檯列印了:
中國人
然後,瀏覽器返回的卻是:
估計是因為SpringMVC是老外寫的吧,不支援中文。
訊息轉換器
在SpringMVC裡有一個訊息轉換器(MessageConverter)的概念,顧名思義就是對輸入輸出值進行轉換。
在抽象類WebMvcConfigurerAdapter中有一個可以重寫的方法,可以配置我們自己的訊息轉換器。
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
當然,SpringMVC裡有自己的訊息轉換器,我們跟蹤configureMessageConverters就能找到。
configureMessageConverters方法在WebMvcConfigurationSupport類中被呼叫:
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
//如果訊息轉換器列表為null,初始化
this .messageConverters = new ArrayList<HttpMessageConverter<?>>();
//配置我們自己的訊息轉換器
configureMessageConverters(this.messageConverters);
//如果我們自己沒有配置,配置預設的訊息轉換器
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
// 追加其他配置
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
有人就要問了,可以自定義訊息轉換器,為什麼還要extendMessageConverters呢?官方文件上是這樣說的,這個方法多用於如果使用了預設的訊息轉換器配置,又需要自定義我們的訊息轉換器,不由得感慨,Spring的貢獻者真是想得周到。
SpringMVC預設的訊息轉換器配置的原始碼如下:
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(
Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build()));
}
else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
messageConverters.add(new MappingJackson2HttpMessageConverter(
Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build()));
}
else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}
自定義訊息轉換器
字串訊息轉換器
回到我們的問題。介面返回的值是亂碼,原因肯定是因為預設訊息轉換器不支援中文,需要我們自定義自定義訊息轉換器。
如果要自定義JSON訊息轉換器,需要引入新的包,在pom.xml引入:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.0</version>
</dependency>
在ServletConfig重寫configureMessageConverters()方法:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter converter = new StringHttpMessageConverter();
Charset urf8 = Charset.forName("UTF-8");
//設定字串轉換器的MediaType
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain", urf8)));
//設定字串轉換器的編碼
converter.setDefaultCharset(urf8);
converters.add(converter);
}
重啟專案後重新請求,返回就沒有問題了。
JSON訊息轉換器
我們的例子演示了字串訊息轉換器的使用,那麼如果返回的是JSON格式的資料呢,肯定需要一個JSON訊息轉換器。
我們在RootConfig中定義一個Bean:
@Bean
public ObjectMapper objectMapper() {
String datetimeFormat = "yyyy-MM-dd HH:mm:ss";
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(datetimeFormat)));
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(datetimeFormat)));
return Jackson2ObjectMapperBuilder
.json()
.modules(javaTimeModule)
.featuresToDisable(WRITE_DATES_AS_TIMESTAMPS)
.dateFormat(new SimpleDateFormat(datetimeFormat)) // Date物件
.build();
}
訊息轉換連結串列中加入JSON訊息轉換器:
//注入Object解析器
@Autowired
private ObjectMapper objectMapper;
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter converter = new StringHttpMessageConverter();
Charset urf8 = Charset.forName("UTF-8");
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain", urf8)));
converter.setDefaultCharset(urf8);
//新增字串訊息轉換器
converters.add(converter);
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
jsonConverter.setDefaultCharset(urf8);
jsonConverter.setObjectMapper(objectMapper);
//新增JSON訊息轉換器
converters.add(jsonConverter);
}
其他轉換器
還有很多其他訊息轉換器,比如XML訊息轉換器等等,由於我們用的比較少,這裡就不介紹了,感興趣的可以參考SpringMVC的官方文件。