1. 程式人生 > >零XML配置 SpringMVC 進階之路

零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的官方文件